diff --git a/.changeset/config.json b/.changeset/config.json index 7fcf97338..9375d80a4 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,11 +1,11 @@ { - "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [], - "linked": [], - "access": "restricted", - "baseBranch": "main", - "updateInternalDependencies": "patch", - "ignore": [] + "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] } diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index fd508cafb..71570693f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,9 +11,10 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: -1. -2. -3. + +1. +2. +3. **Expected behavior** A clear and concise description of what you expected to happen. @@ -23,10 +23,11 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Environment (please complete the following information):** - - OS: [e.g. Mac OS 13.0] - - Node version: [e.g. 16.15.0] - - NPM version: [e.g. 8.19.2] - - Database type: [e.g. Postgresql] + +- OS: [e.g. Mac OS 13.0] +- Node version: [e.g. 16.15.0] +- NPM version: [e.g. 8.19.2] +- Database type: [e.g. Postgresql] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..2f28cead0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4a761f044..1df0c24d8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -9,9 +9,9 @@ env: on: push: - branches: ['dev', 'main'] + branches: ['dev', 'main', 'canary'] pull_request: - branches: ['dev', 'main'] + branches: ['dev', 'main', 'canary'] jobs: build: @@ -34,5 +34,12 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'pnpm' - run: pnpm install --frozen-lockfile - - run: pnpm run build + - run: | + if [[ $GITHUB_REF == 'refs/heads/canary' ]]; then + DEFAULT_NPM_TAG=canary pnpm run build + else + DEFAULT_NPM_TAG=latest pnpm run build + fi + # install again for internal dependencies + - run: pnpm install --frozen-lockfile - run: pnpm run test diff --git a/.gitignore b/.gitignore index 9ddc45ba5..63ef3c421 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist .DS_Store .vscode/settings.json .env.local +.npmcache diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..5203be3ce --- /dev/null +++ b/.prettierignore @@ -0,0 +1,17 @@ +.git +node_modules +dist +generated +.vercel +.next + +package.json +package-lock.json +pnpm-*.yaml + +test-run +coverage +.vscode + +samples/todo/lib/hooks +samples/todo/server/routers/generated \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..4af1219e6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 4, + "useTabs": false, + "printWidth": 120, + "singleQuote": true +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 183ed7c8b..36a98f2b2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,5 +3,5 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. - "recommendations": ["langium.langium-vscode"] + "recommendations": ["langium.langium-vscode", "firsttris.vscode-jest-runner"] } diff --git a/.vscode/launch.json b/.vscode/launch.json index f9d9ea784..45d27c896 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,15 +7,17 @@ "configurations": [ { "name": "Generate for Todo Sample", - "program": "${workspaceFolder}/packages/schema/bin/cli", + "program": "${workspaceFolder}/packages/schema/dist/bin/cli", "cwd": "${workspaceFolder}/samples/todo/", "args": [ - "generate", - "${workspaceFolder}/samples/todo/schema.zmodel" + "generate" ], "request": "launch", "skipFiles": ["/**"], - "type": "node" + "type": "node", + "env": { + "NODE_PATH": "${workspaceFolder}/samples/todo/node_modules" + } }, { "name": "Attach", @@ -40,6 +42,13 @@ "skipFiles": ["/**"], "sourceMaps": true, "outFiles": ["${workspaceFolder}/bundle/**/*.js"] + }, + { + "name": "Todo sample: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "pnpm dev", + "cwd": "${workspaceFolder}/samples/todo/" } ] } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5e740ac1c..212eb48c9 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,24 +17,24 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/README.md b/README.md index 3d763556c..2104ff0eb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-
@@ -19,29 +19,99 @@ ## What it is -ZenStack is a toolkit for building secure CRUD apps with Next.js + Typescript. It lets you define data models, relations and access policies all in one place, and generates database schema, backend CRUD services and frontend React hooks for you automatically. +ZenStack is a toolkit that simplifies the development of a web app's backend. It supercharges [Prisma ORM](https://prisma.io) with a powerful access control layer and unleashes its full potential for web development. Our goal is to let you save time writing boilerplate code and focus on building real features! +## How it works + +ZenStack extended Prisma schema language for supporting custom attributes and functions and, based on that, implemented a flexible access control layer around Prisma. + +```prisma +// schema.zmodel + +model Post { + id String @id + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String + + // 🔐 allow logged-in users to read published posts + @@allow('read', auth() != null && published) + + // 🔐 allow full CRUD by author + @@allow('all', author == auth()) +} +``` + +At runtime, transparent proxies are created around Prisma clients for intercepting queries and mutations to enforce access policies. Moreover, framework integration packages help you wrap an access-control-enabled Prisma client into backend APIs that can be safely called from the frontend. + +```ts +// Next.js example: pages/api/model/[...path].ts + +import { requestHandler } from '@zenstackhq/next'; +import { withPolicy } from '@zenstackhq/runtime'; +import { getSessionUser } from '@lib/auth'; +import { prisma } from '@lib/db'; + +export default requestHandler({ + getPrisma: (req, res) => withPolicy(prisma, { user: getSessionUser(req, res) }), +}); +``` + +Plugins can generate strong-typed client libraries that talk to the APIs: + +```tsx +// React example: components/MyPosts.tsx + +import { usePost } from '@lib/hooks'; + +const MyPosts = () => { + // Post CRUD hooks + const { findMany } = usePost(); + + // list all posts that're visible to the current user, together with their authors + const { data: posts } = findMany({ + include: { author: true }, + orderBy: { createdAt: 'desc' }, + }); + + return ( +
    + {posts?.map((post) => ( +
  • + {post.title} by {post.author.name} +
  • + ))} +
+ ); +}; +``` + ## Links -- [Documentation](https://zenstack.dev) +- [Home](https://zenstack.dev) +- [Documentation](https://zenstack.dev/docs) - [Community chat](https://go.zenstack.dev/chat) - [Twitter](https://twitter.com/zenstackhq) - [Blog](https://dev.to/zenstack) ## Features -- Intuitive data & authorization modeling language -- Generating RESTful CRUD services and React hooks +- Access control and data validation rules right inside your Prisma schema +- Auto-generated RESTful API and client library - End-to-end type safety -- Support for all major relational databases -- Integration with authentication libraries (like [NextAuth](https://next-auth.js.org/) and [iron-session](https://www.npmjs.com/package/iron-session)) -- [VSCode extension](https://marketplace.visualstudio.com/items?itemName=zenstack.zenstack) for model authoring +- Extensible: custom attributes, functions, and a plugin system +- Framework agnostic +- Uncompromised performance ## Examples -Check out the [Collaborative Todo App](https://zenstack-todo.vercel.app/) for a running example. You can find the source code [here](https://github.com/zenstackhq/zenstack/tree/main/samples/todo). +Check out the [Collaborative Todo App](https://zenstack-todo.vercel.app/) for a running example. You can find the source code below: + +- [Next.js + React hooks implementation](https://github.com/zenstackhq/sample-todo-nextjs) +- [Next.js + tRPC implementation](https://github.com/zenstackhq/sample-todo-trpc) ## Community diff --git a/SECURITY.md b/SECURITY.md index 4998472ff..36c4089e2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ | Version | Supported | | ------- | ------------------ | -| >=0.4.0 | :white_check_mark: | +| >=0.4.0 | :white_check_mark: | ## Reporting a Vulnerability diff --git a/doc-serve/.eslintrc.json b/doc-serve/.eslintrc.json deleted file mode 100644 index bffb357a7..000000000 --- a/doc-serve/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/doc-serve/middleware.ts b/doc-serve/middleware.ts deleted file mode 100644 index c20bf1632..000000000 --- a/doc-serve/middleware.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; - -export const config = { - matcher: '/:path*', -}; - -const botUAPattern = /.*(Googlebot|bingbot).*/i; - -export default async function middleware(req: NextRequest) { - const url = req.nextUrl; - - if (url.pathname.includes('.')) { - return NextResponse.next(); - } - - const ua = req.headers.get('user-agent'); - - if (ua && botUAPattern.test(ua)) { - const trimmed = url.pathname.replace(/^\//, ''); - if (trimmed === '' || trimmed === '/') { - url.pathname = '/static/index.html'; - } else { - url.pathname = `/static/${trimmed}.html`; - } - console.log(`REWRITE: ua - ${ua}, url - ${url.pathname}`); - return NextResponse.rewrite(url); - } else { - url.pathname = '/index.html'; - return NextResponse.rewrite(url); - } -} diff --git a/doc-serve/package-lock.json b/doc-serve/package-lock.json deleted file mode 100644 index 40af89b5d..000000000 --- a/doc-serve/package-lock.json +++ /dev/null @@ -1,5107 +0,0 @@ -{ - "name": "doc-serve", - "version": "0.1.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "doc-serve", - "version": "0.1.0", - "dependencies": { - "@types/node": "18.11.13", - "@types/react": "18.0.26", - "@types/react-dom": "18.0.9", - "eslint": "8.29.0", - "eslint-config-next": "13.0.6", - "next": "13.0.6", - "react": "18.2.0", - "react-dom": "18.2.0", - "typescript": "4.9.4" - } - }, - "node_modules/@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", - "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", - "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" - }, - "node_modules/@next/env": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.6.tgz", - "integrity": "sha512-yceT6DCHKqPRS1cAm8DHvDvK74DLIkDQdm5iV+GnIts8h0QbdHvkUIkdOvQoOODgpr6018skbmSQp12z5OWIQQ==" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.6.tgz", - "integrity": "sha512-JUANdYNCddhmQBjQQPxEJYL7GMCqYtbfrdmtX7c013srig7waNCG69Aoql7CgAgjdy8jn1ovHVdcF/NB46XN3Q==", - "dependencies": { - "glob": "7.1.7" - } - }, - "node_modules/@next/swc-android-arm-eabi": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.6.tgz", - "integrity": "sha512-FGFSj3v2Bluw8fD/X+1eXIEB0PhoJE0zfutsAauRhmNpjjZshLDgoXMWm1jTRL/04K/o9gwwO2+A8+sPVCH1uw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.6.tgz", - "integrity": "sha512-7MgbtU7kimxuovVsd7jSJWMkIHBDBUsNLmmlkrBRHTvgzx5nDBXogP0hzZm7EImdOPwVMPpUHRQMBP9mbsiJYQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.6.tgz", - "integrity": "sha512-AUVEpVTxbP/fxdFsjVI9d5a0CFn6NVV7A/RXOb0Y+pXKIIZ1V5rFjPwpYfIfyOo2lrqgehMNQcyMRoTrhq04xg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.6.tgz", - "integrity": "sha512-SasCDJlshglsPnbzhWaIF6VEGkQy2NECcAOxPwaPr0cwbbt4aUlZ7QmskNzgolr5eAjFS/xTr7CEeKJtZpAAtQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-freebsd-x64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.6.tgz", - "integrity": "sha512-6Lbxd9gAdXneTkwHyYW/qtX1Tdw7ND9UbiGsGz/SP43ZInNWnW6q0au4hEVPZ9bOWWRKzcVoeTBdoMpQk9Hx9w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.6.tgz", - "integrity": "sha512-wNdi5A519e1P+ozEuYOhWPzzE6m1y7mkO6NFwn6watUwO0X9nZs7fT9THmnekvmFQpaZ6U+xf2MQ9poQoCh6jQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.6.tgz", - "integrity": "sha512-e8KTRnleQY1KLk5PwGV5hrmvKksCc74QRpHl5ffWnEEAtL2FE0ave5aIkXqErsPdXkiKuA/owp3LjQrP+/AH7Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.6.tgz", - "integrity": "sha512-/7RF03C3mhjYpHN+pqOolgME3guiHU5T3TsejuyteqyEyzdEyLHod+jcYH6ft7UZ71a6TdOewvmbLOtzHW2O8A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.6.tgz", - "integrity": "sha512-kxyEXnYHpOEkFnmrlwB1QlzJtjC6sAJytKcceIyFUHbCaD3W/Qb5tnclcnHKTaFccizZRePXvV25Ok/eUSpKTw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.6.tgz", - "integrity": "sha512-N0c6gubS3WW1oYYgo02xzZnNatfVQP/CiJq2ax+DJ55ePV62IACbRCU99TZNXXg+Kos6vNW4k+/qgvkvpGDeyA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.6.tgz", - "integrity": "sha512-QjeMB2EBqBFPb/ac0CYr7GytbhUkrG4EwFWbcE0vsRp4H8grt25kYpFQckL4Jak3SUrp7vKfDwZ/SwO7QdO8vw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.6.tgz", - "integrity": "sha512-EQzXtdqRTcmhT/tCq81rIwE36Y3fNHPInaCuJzM/kftdXfa0F+64y7FAoMO13npX8EG1+SamXgp/emSusKrCXg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.6.tgz", - "integrity": "sha512-pSkqZ//UP/f2sS9T7IvHLfEWDPTX0vRyXJnAUNisKvO3eF3e1xdhDX7dix/X3Z3lnN4UjSwOzclAI87JFbOwmQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgr/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", - "dependencies": { - "cross-spawn": "^7.0.3", - "is-glob": "^4.0.3", - "open": "^8.4.0", - "picocolors": "^1.0.0", - "tiny-glob": "^0.2.9", - "tslib": "^2.4.0" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" - }, - "node_modules/@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "node_modules/@types/node": { - "version": "18.11.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz", - "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "node_modules/@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.0.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", - "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", - "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", - "dependencies": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", - "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", - "dependencies": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", - "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", - "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", - "dependencies": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", - "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", - "dependencies": { - "@typescript-eslint/types": "5.46.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" - }, - "node_modules/axe-core": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", - "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/core-js-pure": { - "version": "3.26.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", - "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-abstract": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", - "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "unbox-primitive": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", - "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.6.tgz", - "integrity": "sha512-Tfn/0lirhkEuoGxKMtDQNtQuC7P3eHcyUyhIJY/OHtjU9ExHFtcge/Fe8Ou/Jd7DIC71vN3CT72oszVwia71cg==", - "dependencies": { - "@next/eslint-plugin-next": "13.0.6", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.42.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.2.tgz", - "integrity": "sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==", - "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.10.0", - "get-tsconfig": "^4.2.0", - "globby": "^13.1.2", - "is-core-module": "^2.10.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "dependencies": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.31.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", - "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", - "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==", - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "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/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "node_modules/language-tags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", - "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", - "dependencies": { - "language-subtag-registry": "^0.3.20" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/next/-/next-13.0.6.tgz", - "integrity": "sha512-COvigvms2LRt1rrzfBQcMQ2GZd86Mvk1z+LOLY5pniFtL4VrTmhZ9salrbKfSiXbhsD01TrDdD68ec3ABDyscA==", - "dependencies": { - "@next/env": "13.0.6", - "@swc/helpers": "0.4.14", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.0" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=14.6.0" - }, - "optionalDependencies": { - "@next/swc-android-arm-eabi": "13.0.6", - "@next/swc-android-arm64": "13.0.6", - "@next/swc-darwin-arm64": "13.0.6", - "@next/swc-darwin-x64": "13.0.6", - "@next/swc-freebsd-x64": "13.0.6", - "@next/swc-linux-arm-gnueabihf": "13.0.6", - "@next/swc-linux-arm64-gnu": "13.0.6", - "@next/swc-linux-arm64-musl": "13.0.6", - "@next/swc-linux-x64-gnu": "13.0.6", - "@next/swc-linux-x64-musl": "13.0.6", - "@next/swc-win32-arm64-msvc": "13.0.6", - "@next/swc-win32-ia32-msvc": "13.0.6", - "@next/swc-win32-x64-msvc": "13.0.6" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^6.0.0 || ^7.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "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.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", - "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", - "integrity": "sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw==", - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/runtime-corejs3": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", - "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", - "requires": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.11" - } - }, - "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.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" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" - }, - "@next/env": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.6.tgz", - "integrity": "sha512-yceT6DCHKqPRS1cAm8DHvDvK74DLIkDQdm5iV+GnIts8h0QbdHvkUIkdOvQoOODgpr6018skbmSQp12z5OWIQQ==" - }, - "@next/eslint-plugin-next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.6.tgz", - "integrity": "sha512-JUANdYNCddhmQBjQQPxEJYL7GMCqYtbfrdmtX7c013srig7waNCG69Aoql7CgAgjdy8jn1ovHVdcF/NB46XN3Q==", - "requires": { - "glob": "7.1.7" - } - }, - "@next/swc-android-arm-eabi": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.6.tgz", - "integrity": "sha512-FGFSj3v2Bluw8fD/X+1eXIEB0PhoJE0zfutsAauRhmNpjjZshLDgoXMWm1jTRL/04K/o9gwwO2+A8+sPVCH1uw==", - "optional": true - }, - "@next/swc-android-arm64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.6.tgz", - "integrity": "sha512-7MgbtU7kimxuovVsd7jSJWMkIHBDBUsNLmmlkrBRHTvgzx5nDBXogP0hzZm7EImdOPwVMPpUHRQMBP9mbsiJYQ==", - "optional": true - }, - "@next/swc-darwin-arm64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.6.tgz", - "integrity": "sha512-AUVEpVTxbP/fxdFsjVI9d5a0CFn6NVV7A/RXOb0Y+pXKIIZ1V5rFjPwpYfIfyOo2lrqgehMNQcyMRoTrhq04xg==", - "optional": true - }, - "@next/swc-darwin-x64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.6.tgz", - "integrity": "sha512-SasCDJlshglsPnbzhWaIF6VEGkQy2NECcAOxPwaPr0cwbbt4aUlZ7QmskNzgolr5eAjFS/xTr7CEeKJtZpAAtQ==", - "optional": true - }, - "@next/swc-freebsd-x64": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.6.tgz", - "integrity": "sha512-6Lbxd9gAdXneTkwHyYW/qtX1Tdw7ND9UbiGsGz/SP43ZInNWnW6q0au4hEVPZ9bOWWRKzcVoeTBdoMpQk9Hx9w==", - "optional": true - }, - "@next/swc-linux-arm-gnueabihf": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.6.tgz", - "integrity": "sha512-wNdi5A519e1P+ozEuYOhWPzzE6m1y7mkO6NFwn6watUwO0X9nZs7fT9THmnekvmFQpaZ6U+xf2MQ9poQoCh6jQ==", - "optional": true - }, - "@next/swc-linux-arm64-gnu": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.6.tgz", - "integrity": "sha512-e8KTRnleQY1KLk5PwGV5hrmvKksCc74QRpHl5ffWnEEAtL2FE0ave5aIkXqErsPdXkiKuA/owp3LjQrP+/AH7Q==", - "optional": true - }, - "@next/swc-linux-arm64-musl": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.6.tgz", - "integrity": "sha512-/7RF03C3mhjYpHN+pqOolgME3guiHU5T3TsejuyteqyEyzdEyLHod+jcYH6ft7UZ71a6TdOewvmbLOtzHW2O8A==", - "optional": true - }, - "@next/swc-linux-x64-gnu": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.6.tgz", - "integrity": "sha512-kxyEXnYHpOEkFnmrlwB1QlzJtjC6sAJytKcceIyFUHbCaD3W/Qb5tnclcnHKTaFccizZRePXvV25Ok/eUSpKTw==", - "optional": true - }, - "@next/swc-linux-x64-musl": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.6.tgz", - "integrity": "sha512-N0c6gubS3WW1oYYgo02xzZnNatfVQP/CiJq2ax+DJ55ePV62IACbRCU99TZNXXg+Kos6vNW4k+/qgvkvpGDeyA==", - "optional": true - }, - "@next/swc-win32-arm64-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.6.tgz", - "integrity": "sha512-QjeMB2EBqBFPb/ac0CYr7GytbhUkrG4EwFWbcE0vsRp4H8grt25kYpFQckL4Jak3SUrp7vKfDwZ/SwO7QdO8vw==", - "optional": true - }, - "@next/swc-win32-ia32-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.6.tgz", - "integrity": "sha512-EQzXtdqRTcmhT/tCq81rIwE36Y3fNHPInaCuJzM/kftdXfa0F+64y7FAoMO13npX8EG1+SamXgp/emSusKrCXg==", - "optional": true - }, - "@next/swc-win32-x64-msvc": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.6.tgz", - "integrity": "sha512-pSkqZ//UP/f2sS9T7IvHLfEWDPTX0vRyXJnAUNisKvO3eF3e1xdhDX7dix/X3Z3lnN4UjSwOzclAI87JFbOwmQ==", - "optional": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgr/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", - "requires": { - "cross-spawn": "^7.0.3", - "is-glob": "^4.0.3", - "open": "^8.4.0", - "picocolors": "^1.0.0", - "tiny-glob": "^0.2.9", - "tslib": "^2.4.0" - } - }, - "@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" - }, - "@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", - "requires": { - "tslib": "^2.4.0" - } - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "@types/node": { - "version": "18.11.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.13.tgz", - "integrity": "sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w==" - }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "18.0.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", - "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", - "requires": { - "@types/react": "*" - } - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "@typescript-eslint/parser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", - "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", - "requires": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", - "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", - "requires": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0" - } - }, - "@typescript-eslint/types": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", - "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==" - }, - "@typescript-eslint/typescript-estree": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", - "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", - "requires": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", - "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", - "requires": { - "@typescript-eslint/types": "5.46.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" - }, - "axe-core": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", - "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==" - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "caniuse-lite": { - "version": "1.0.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "core-js-pure": { - "version": "3.26.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", - "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "es-abstract": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", - "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "unbox-primitive": "^1.0.2" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - } - }, - "eslint-config-next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.6.tgz", - "integrity": "sha512-Tfn/0lirhkEuoGxKMtDQNtQuC7P3eHcyUyhIJY/OHtjU9ExHFtcge/Fe8Ou/Jd7DIC71vN3CT72oszVwia71cg==", - "requires": { - "@next/eslint-plugin-next": "13.0.6", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.42.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-import-resolver-typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.2.tgz", - "integrity": "sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==", - "requires": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.10.0", - "get-tsconfig": "^4.2.0", - "globby": "^13.1.2", - "is-core-module": "^2.10.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.4" - }, - "dependencies": { - "globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" - } - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "requires": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "eslint-plugin-react": { - "version": "7.31.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", - "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==", - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-tsconfig": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", - "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==" - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", - "requires": { - "type-fest": "^0.20.2" - } - }, - "globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "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" - } - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "language-tags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", - "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", - "requires": { - "language-subtag-registry": "^0.3.20" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "next": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/next/-/next-13.0.6.tgz", - "integrity": "sha512-COvigvms2LRt1rrzfBQcMQ2GZd86Mvk1z+LOLY5pniFtL4VrTmhZ9salrbKfSiXbhsD01TrDdD68ec3ABDyscA==", - "requires": { - "@next/env": "13.0.6", - "@next/swc-android-arm-eabi": "13.0.6", - "@next/swc-android-arm64": "13.0.6", - "@next/swc-darwin-arm64": "13.0.6", - "@next/swc-darwin-x64": "13.0.6", - "@next/swc-freebsd-x64": "13.0.6", - "@next/swc-linux-arm-gnueabihf": "13.0.6", - "@next/swc-linux-arm64-gnu": "13.0.6", - "@next/swc-linux-arm64-musl": "13.0.6", - "@next/swc-linux-x64-gnu": "13.0.6", - "@next/swc-linux-x64-musl": "13.0.6", - "@next/swc-win32-arm64-msvc": "13.0.6", - "@next/swc-win32-ia32-msvc": "13.0.6", - "@next/swc-win32-x64-msvc": "13.0.6", - "@swc/helpers": "0.4.14", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "requires": { - "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.3" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "requires": { - "p-limit": "^3.0.2" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "styled-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", - "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", - "requires": { - "client-only": "0.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "synckit": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", - "integrity": "sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw==", - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.4.0" - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "requires": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" - } - } -} diff --git a/doc-serve/package.json b/doc-serve/package.json deleted file mode 100644 index cd602b5c1..000000000 --- a/doc-serve/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "doc-serve", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@types/node": "18.11.13", - "@types/react": "18.0.26", - "@types/react-dom": "18.0.9", - "eslint": "8.29.0", - "eslint-config-next": "13.0.6", - "next": "13.0.6", - "react": "18.2.0", - "react-dom": "18.2.0", - "typescript": "4.9.4" - } -} diff --git a/doc-serve/public/8cd31ff7afe74424abbec00b65f04ae8.txt b/doc-serve/public/8cd31ff7afe74424abbec00b65f04ae8.txt deleted file mode 100644 index 4c8237722..000000000 --- a/doc-serve/public/8cd31ff7afe74424abbec00b65f04ae8.txt +++ /dev/null @@ -1 +0,0 @@ -8cd31ff7afe74424abbec00b65f04ae8 diff --git a/doc-serve/public/README.md b/doc-serve/public/README.md deleted file mode 100644 index 50e6e9cc5..000000000 --- a/doc-serve/public/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# ZenStack - -> A toolkit for building secure CRUD apps with Next.js. - -## What it is - -ZenStack is a schema-first toolkit for defining data models, relations and access policies. It generates database schema, backend CRUD services and frontend React hooks for you automatically from the model. Our goal is to let you save time writing boilerplate code and focus on building real features! - -_NOTE_: ZenStack is built above [Prisma ORM](https://www.prisma.io/) - the greatest ORM solution for Typescript. It extends Prisma's power from database handling to full-stack development. - -See the [Quick start](quick-start.md) guide for more details. - -## Features - -- Intuitive data & authorization modeling language -- Generating RESTful CRUD services and React hooks -- End-to-end type safety -- Support for [all major relational databases](zmodel-data-source.md#supported-databases) -- Integration with authentication libraries (like [NextAuth](https://next-auth.js.org/ ':target=_blank')) -- [VSCode extension](https://marketplace.visualstudio.com/items?itemName=zenstack.zenstack ':target=_blank') for model authoring - -## Examples - -Check out the [Collaborative Todo App](https://zenstack-todo.vercel.app/ ':target=_blank') for a running example. You can find the source code [here](https://github.com/zenstackhq/todo-demo-sqlite ':target=_blank'). - -## Community - -Join our [discord server](https://go.zenstack.dev/chat ':target=_blank') for chat and updates! diff --git a/doc-serve/public/_coverpage.md b/doc-serve/public/_coverpage.md deleted file mode 100644 index 539dc966a..000000000 --- a/doc-serve/public/_coverpage.md +++ /dev/null @@ -1,12 +0,0 @@ -![cover-logo](_media/logo.png) - -# ZenStack 0.4.0 - -> A toolkit for building secure CRUD apps with Next.js + Typescript. - -- Full-stack toolkit made for front-end developers -- Intuitive and flexible data modeling -- No more boilerplate CRUD code - -[GitHub](https://github.com/zenstackhq/zenstack/) -[Get Started](#zenstack) diff --git a/doc-serve/public/_media/banner.png b/doc-serve/public/_media/banner.png deleted file mode 100644 index b2074d34e..000000000 Binary files a/doc-serve/public/_media/banner.png and /dev/null differ diff --git a/doc-serve/public/_media/cli-shot.png b/doc-serve/public/_media/cli-shot.png deleted file mode 100644 index 6f7fdbc6e..000000000 Binary files a/doc-serve/public/_media/cli-shot.png and /dev/null differ diff --git a/doc-serve/public/_media/logo.png b/doc-serve/public/_media/logo.png deleted file mode 100644 index 7358d53b6..000000000 Binary files a/doc-serve/public/_media/logo.png and /dev/null differ diff --git a/doc-serve/public/_media/starter-shot.png b/doc-serve/public/_media/starter-shot.png deleted file mode 100644 index d6531d8b1..000000000 Binary files a/doc-serve/public/_media/starter-shot.png and /dev/null differ diff --git a/doc-serve/public/_navbar.md b/doc-serve/public/_navbar.md deleted file mode 100644 index 1c416471c..000000000 --- a/doc-serve/public/_navbar.md +++ /dev/null @@ -1 +0,0 @@ -- [Live Chat](https://go.zenstack.dev/chat ':target=_blank') diff --git a/doc-serve/public/_sidebar.md b/doc-serve/public/_sidebar.md deleted file mode 100644 index 7e1be8536..000000000 --- a/doc-serve/public/_sidebar.md +++ /dev/null @@ -1,38 +0,0 @@ -- Getting started - - - [Quick start](quick-start.md) - - [Modeling your app](modeling-your-app.md) - - [Code generation](code-generation.md) - - [Building your app](building-your-app.md) - -- ZModel reference - - - [Overview](zmodel-overview.md) - - [Data source](zmodel-data-source.md) - - [Enum](zmodel-enum.md) - - [Data model](zmodel-data-model.md) - - [Attribute](zmodel-attribute.md) - - [Field](zmodel-field.md) - - [Relation](zmodel-relation.md) - - [Access policy](zmodel-access-policy.md) - - [Field constraint](zmodel-field-constraint.md) - - [Referential action](zmodel-referential-action.md) - -- CLI reference - - - [Commands](cli-commands.md) - -- [Runtime API](runtime-api.md) - -- Guide - - - [Choosing a database](choosing-a-database.md) - - [Evolving model with migration](evolving-model-with-migration.md) - - [Integrating authentication](integrating-authentication.md) - - [Server-side rendering](server-side-rendering.md) - - [Set up logging](setup-logging.md) - - [Telemetry](telemetry.md) - -- [VSCode extension](vscode-extension.md) -- [Reach out to the developers](reach-out.md) -- [Changelog](changelog) diff --git a/doc-serve/public/building-your-app.md b/doc-serve/public/building-your-app.md deleted file mode 100644 index e25d70ac6..000000000 --- a/doc-serve/public/building-your-app.md +++ /dev/null @@ -1,149 +0,0 @@ -# Building your app - -The code generated from your model covers everything you need to implement CRUD, frontend and backend. This section illustrates the steps of using them when building your app. - -## Mounting backend services - -First you should mount the generated server-side code as a Next.js API endpoint. Here's an example: - -```ts -// pages/api/zenstack/[...path].ts - -import { authOptions } from '@api/auth/[...nextauth]'; -import service from '@zenstackhq/runtime'; -import { - requestHandler, - type RequestHandlerOptions, -} from '@zenstackhq/runtime/server'; -import { NextApiRequest, NextApiResponse } from 'next'; -import { unstable_getServerSession } from 'next-auth'; - -const options: RequestHandlerOptions = { - // a callback for getting the current login user - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - // here we use NextAuth is used as an example, and you can change it to - // suit the authentication solution you use - const session = await unstable_getServerSession(req, res, authOptions); - return session?.user; - }, -}; -export default requestHandler(service, options); -``` - -Please note that the services need to be configured with a callback `getServerUser` for getting the current login user. The example above uses NextAuth to do it, but you can also hand-code it based on the authentication approach you use, as long as it returns a user object that represents the current session's user. - -_NOTE_ Check out [this guide](integrating-authentication.md) for more details about integrating with authentication. - -Make sure the services are mounted at route `/api/zenstack/` with a catch all parameter named `path`, as this is required by the generate React hooks. - -## _optional_ Integrating with NextAuth - -If you use NextAuth for authentication, you can install the `@zenstackhq/next-auth` package for easier integration. This package provides an adapter and authorization helper that you can use to configure NextAuth. - -```ts -// pages/api/auth/[...nextauth].ts - -import service from '@zenstackhq/runtime'; -import { authorize, Adapter } from '@zenstackhq/next-auth'; -import NextAuth, { type NextAuthOptions } from 'next-auth'; - -export const authOptions: NextAuthOptions = { - // use ZenStack adapter for persistence - adapter: Adapter(service), - - providers: [ - CredentialsProvider({ - credentials: { ... }, - // use the generated "authorize" helper for credential based authentication - authorize: authorize(service), - }), - ] - - ... -}; - -export default NextAuth(authOptions); -``` - -Find more details [here](integrating-authentication.md) about integrating with different authentication schemes. - -## Using React hooks - -React hooks are generated for CRUD'ing each data model you defined. They save your time writing explicit HTTP requests to call the generated services. Internally the hooks use [SWR](https://swr.vercel.app/) for data fetching, so you'll also enjoy its built-in features, like caching, revalidation on interval, etc. - -_NOTE_ The generated service code is injected with the access policies you defined in the model, so it's already secure, regardless called directly or via hooks. A read operation only returns data that's supposed to be visible to the current user, and a write operation is rejected if the policies verdict so. - -### Read - -Call `find` and `get` hooks for listing entities or loading a specific one. If your entity has relations, you can request related entities to be loaded together. - -```ts -const { find } = usePost(); -// lists unpublished posts with their author's data -const posts = find({ - where: { published: false }, - include: { author: true }, - orderBy: { updatedAt: 'desc' }, -}); -``` - -```ts -const { get } = usePost(); -// fetches a post with its author's data -const post = get(id, { - include: { author: true }, -}); -``` - -### Create - -Call the async `create` method to create a new model entity. Note that if the model has relations, you can create related entities in a nested write. See example below: - -```ts -const { create } = usePost(); -// creating a new post for current user with a nested comment -const post = await create({ - data: { - title: 'My New Post', - author: { - connect: { id: session.user.id }, - }, - comments: { - create: [{ content: 'First comment' }], - }, - }, -}); -``` - -### Update - -Similar to `create`, the update hook also allows nested write. - -```ts -const { update } = usePost(); -// updating a post's content and create a new comment -const post = await update(id, { - data: { - const: 'My post content', - comments: { - create: [{ content: 'A new comment' }], - }, - }, -}); -``` - -### Delete - -```ts -const { del } = usePost(); -const post = await del(id); -``` - -## Server-side coding - -Since doing CRUD with hooks is already secure, in many cases, you can implement your business logic right in the frontend code. - -ZenStack also supports server-side programming for conducting CRUD without sending HTTP requests, or even direct database access (bypassing access policy checks). Please check the following documentation for details: - -- [Server runtime API](runtime-api.md#zenstackhqruntimeserver) -- [Server-side rendering](server-side-rendering.md) diff --git a/doc-serve/public/choosing-a-database.md b/doc-serve/public/choosing-a-database.md deleted file mode 100644 index 44bec5d16..000000000 --- a/doc-serve/public/choosing-a-database.md +++ /dev/null @@ -1,11 +0,0 @@ -# Choosing a database - -ZenStack is agnostic about where and how you deploy your web app, but hosting on serverless platforms like [Vercel](https://vercel.com/ ':target=blank') is definitely a popular choice. - -Serverless architecture has some implications on how you should care about your database hosting. Different from traditional architecture where you have a fixed number of long-running Node.js servers, in a serverless environment, a new Node.js context can potentially be created for each user request, and if traffic volume is high, this can quickly exhaust your database's connection limit, if you connect to the database directly without a proxy. - -You'll likely be OK if your app has a low number of concurrent users, otherwise you should consider using a proxy in front of your database server. Here's a number of (incomplete) solutions you can consider: - -- [Prisma Data Proxy](https://www.prisma.io/data-platform/proxy ':target=blank') -- [Supabase](https://supabase.com/)'s [connection pool](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pool ':target=blank') -- [Deploy pgbouncer with Postgres on Heroku](https://devcenter.heroku.com/articles/postgres-connection-pooling ':target=blank') diff --git a/doc-serve/public/cli-commands.md b/doc-serve/public/cli-commands.md deleted file mode 100644 index 92be36169..000000000 --- a/doc-serve/public/cli-commands.md +++ /dev/null @@ -1,131 +0,0 @@ -# CLI commands - -## `init` - -Set up ZenStack for an existing Next.js + Typescript project. - -```bash -npx zenstack init [options] [dir] -``` - -_Options_: - -``` - -p, --package-manager : package manager to use: "npm", "yarn", or "pnpm" (default: auto detect) -``` - -## `generate` - -Generates RESTful CRUD API and React hooks from your model. - -```bash -npx zenstack generate [options] -``` - -_Options_: - -``` - --schema : schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") - - -p, --package-manager : package manager to use: "npm", "yarn", or "pnpm" (default: auto detect) -``` - -## `migrate` - -Update the database schema with migrations. - -**Sub-commands**: - -### `migrate dev` - -Create a migration from changes in Prisma schema, apply it to the database, trigger generation of database client. This command wraps `prisma migrate` command. - -```bash -npx zenstack migrate dev [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") -``` - -### `migrate reset` - -Reset your database and apply all migrations. - -```bash -npx zenstack migrate reset [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") -``` - -### `migrate deploy` - -Apply pending migrations to the database in production/staging. - -```bash -npx zenstack migrate deploy [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") -``` - -### `migrate status` - -Check the status of migrations in the production/staging database. - -```bash -npx zenstack migrate status [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") -``` - -## `db` - -Manage your database schema and lifecycle during development. This command wraps `prisma db` command. - -**Sub-commands**: - -### `db push` - -Push the state from model to the database during prototyping. - -```bash -npx zenstack db push [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") - --accept-data-loss Ignore data loss warnings -``` - -## `studio` - -Browse your data with Prisma Studio. This command wraps `prisma studio` command. - -```bash -npx zenstack studio [options] -``` - -_Options_: - -``` - --schema schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel") - -p --port Port to start Studio in - -b --browser Browser to open Studio in - -n --hostname Hostname to bind the Express server to -``` diff --git a/doc-serve/public/code-generation.md b/doc-serve/public/code-generation.md deleted file mode 100644 index 8db1cbe53..000000000 --- a/doc-serve/public/code-generation.md +++ /dev/null @@ -1,15 +0,0 @@ -# Code generation - -Code generation is where your modeling work pays off. To trigger it, simply run: - -```bash -npx zenstack generate -``` - -You should see an output similar to: - -![CLI screenshot](_media/cli-shot.png 'CLI screenshot') - -A full reference of the `zenstack` CLI can be found [here](cli-references.md), but for now knowing just this one command is good enough. - -As you see, several code generators are run to create pieces of code that help you build the app. Let's see how to use it in the [next section](building-your-app.md). diff --git a/doc-serve/public/entity-types-server.md b/doc-serve/public/entity-types-server.md deleted file mode 100644 index 189df5691..000000000 --- a/doc-serve/public/entity-types-server.md +++ /dev/null @@ -1,84 +0,0 @@ -# Types - -Module `@zenstackhq/runtime/types` contains type definitions of entities, filters, sorting, etc., generated from ZModel data models. The types can be used in both the front-end and the backend code. - -Suppose a `User` model is defined in ZModel: - -```zmodel -model User { - id String @id @default(cuid()) - email String @unique @email - password String @password @omit - name String? - posts Post[] -} -``` - -The following types are generated: - -## Entity type - -````ts -export type User = { - id: string - email: string - password: string | null - name: string | null - posts: Post[] -}``` - -This type serves as the return type of the generated React hooks: - -```ts -import { type User } from '@zenstackhq/runtime/types'; -import { useUser } from '@zenstackhq/runtime/client'; - -export function MyComponent() { - const { find } = useUser(); - const result = find(); - const users: User[] = result.data; - ... -} -```` - -Backend database access API also returns the same type: - -```ts -const users: User[] = await service.db.user.find(); -``` - -## Filter and sort type - -Types for filtering and sorting entites are also generated: - -```ts -export type UserFindManyArgs = { - select?: UserSelect | null; - include?: UserInclude | null; - where?: UserWhereInput; - orderBy?: Enumerable; - ... -}; -``` - -You can use it like: - -```ts -const { find } = useUser(); -const { data: users } = find({ - where: { - email: { - endsWith: '@zenstack.dev', - }, - }, - orderBy: [ - { - email: 'asc', - }, - ], - include: { - // include related Post entities - posts: true, - }, -}); -``` diff --git a/doc-serve/public/entity-types.md b/doc-serve/public/entity-types.md deleted file mode 100644 index 189df5691..000000000 --- a/doc-serve/public/entity-types.md +++ /dev/null @@ -1,84 +0,0 @@ -# Types - -Module `@zenstackhq/runtime/types` contains type definitions of entities, filters, sorting, etc., generated from ZModel data models. The types can be used in both the front-end and the backend code. - -Suppose a `User` model is defined in ZModel: - -```zmodel -model User { - id String @id @default(cuid()) - email String @unique @email - password String @password @omit - name String? - posts Post[] -} -``` - -The following types are generated: - -## Entity type - -````ts -export type User = { - id: string - email: string - password: string | null - name: string | null - posts: Post[] -}``` - -This type serves as the return type of the generated React hooks: - -```ts -import { type User } from '@zenstackhq/runtime/types'; -import { useUser } from '@zenstackhq/runtime/client'; - -export function MyComponent() { - const { find } = useUser(); - const result = find(); - const users: User[] = result.data; - ... -} -```` - -Backend database access API also returns the same type: - -```ts -const users: User[] = await service.db.user.find(); -``` - -## Filter and sort type - -Types for filtering and sorting entites are also generated: - -```ts -export type UserFindManyArgs = { - select?: UserSelect | null; - include?: UserInclude | null; - where?: UserWhereInput; - orderBy?: Enumerable; - ... -}; -``` - -You can use it like: - -```ts -const { find } = useUser(); -const { data: users } = find({ - where: { - email: { - endsWith: '@zenstack.dev', - }, - }, - orderBy: [ - { - email: 'asc', - }, - ], - include: { - // include related Post entities - posts: true, - }, -}); -``` diff --git a/doc-serve/public/evolving-model-with-migration.md b/doc-serve/public/evolving-model-with-migration.md deleted file mode 100644 index e4354324b..000000000 --- a/doc-serve/public/evolving-model-with-migration.md +++ /dev/null @@ -1,65 +0,0 @@ -# Evolving model with migration - -When using ZenStack, your schema.zmodel file represents the current status of your app's data model and your database's schema. When you make changes to schema.zmodel, however, your data model drifts away from database schema. At your app's deployment time, such drift needs to be "fixed", and so that your database schema stays synchronized with your data model. This processing of "fixing" is called migration. - -Here we summarize a few common scenarios and show how you should work on migration. - -## For a newly created schema.zmodel - -When you're just starting out a ZenStack project, you have an empty migration history. After creating schema.zmodel, adding a development datasource and adding some models, run the following command to bootstrap your migration history and synchronize your development database schema: - -```bash -npx zenstack migrate dev -n init -``` - -After it's run, you should find a folder named `migrations` created under `zenstack` folder, inside of which you can find a .sql file containing script that initializes your database. Please note that when you run "migration dev", the generated migration script is automatically run agains your datasource specified in schema.zmodel. - -Make sure you commit the `migrations` folder into source control. - -## After updating an existing schema.zmodel - -After making update to schema.zmodel, run the "migrate dev" command to generate an incremental migration record: - -```bash -npx zenstack migrate dev -n [short-name-for-the-change] -``` - -If any database schema change is needed based on the previous version of data model, a new .sql file will be generated under `zenstack/migrations` folder. Your development database's schema is automatically synchronized after running the command. - -Make sure you review that the generated .sql script reflects your intention before committing it to source control. - -## Pushing model changes to database without creating migration - -This is helpful when you're prototyping locally and don't want to create migration records. Simply run: - -```bash -npx zenstack db push -``` - -, and your database schema will be synced with schema.zmodel. After prototyping, reset your local database and generate migration records: - -```bash -npx zenstack migrate reset -``` - -```bash -npx zenstack migrate dev -n [name] -``` - -### During deployment - -When deploying your app to an official environment (a shared dev environment, staging, or production), **DO NOT** run `migrate dev` command in CI scripts. Instead, run `migrate deploy`. - -```bash -npx zenstack migrate deploy -``` - -The `migrate deploy` command does not generate new migration records. It simply detects records that are created after the previous deployment and execute them in order. As a result, your database schema is synchronized with data model. - -If you've always been taking the "migrate dev" and "migrate deploy" loop during development, your migration should run smoothly. However manually changing db schema, manually changing/deleting migration records can result in failure during migration. Please refer to this documentation for [troubleshooting migration issues in production](https://www.prisma.io/docs/guides/database/production-troubleshooting). - -## Summary - -ZenStack is built over [Prisma](https://www.prisma.io ':target=blank') and it internally delegates all ORM tasks to Prisma. The migration workflow is exactly the same as Prisma's workflow, with the only exception that the source of input is schema.zmodel, and a Prisma schema is generated on the fly. The set of migration commands that ZModel CLI offers, like "migrate dev" and "migrate deploy", are simple wrappers around Prisma commands. - -Prisma has [excellent documentation](https://www.prisma.io/docs/concepts/components/prisma-migrate ':target=blank') about migration. Make sure you look into those for a more thorough understanding. diff --git a/doc-serve/public/index.html b/doc-serve/public/index.html deleted file mode 100644 index 65b47cac0..000000000 --- a/doc-serve/public/index.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - ZenStack - toolkit for building CRUD apps with Next.js + Typescript - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please wait...
- - - - - - - - - - - - - diff --git a/doc-serve/public/integrating-authentication.md b/doc-serve/public/integrating-authentication.md deleted file mode 100644 index bff6a3154..000000000 --- a/doc-serve/public/integrating-authentication.md +++ /dev/null @@ -1,228 +0,0 @@ -# Integrating authentication - -This documentation explains how to integrate ZenStack with popular authentication frameworks. - -## NextAuth - -[NextAuth](https://next-auth.js.org/) is a comprehensive framework for implementating authentication. It offers a pluggable mechanism for configuring how user data is persisted. You can find a full example using ZenStack with NextAuth [here](https://github.com/zenstackhq/zenstack/tree/main/samples/todo ':target=blank'). - -ZenStack provides an extension package `@zenstackhq/next-auth` for integrating with NextAuth, which includes: - -### Adapter - -Adapter is a NextAuth mechanism for hooking in custom persistence of auth related entities, like User, Account, etc. The ZenStack adapter can be configured to NextAuth as follows: - -```ts -// pages/api/auth/[...nextauth].ts - -import service from '@zenstackhq/runtime/server'; -import { Adapter } from '@zenstackhq/next-auth'; -import NextAuth, { type NextAuthOptions } from 'next-auth'; - -export const authOptions: NextAuthOptions = { - // install ZenStack adapter - adapter: Adapter(service), - ... -}; - -export default NextAuth(authOptions); -``` - -### `authorize` helper - -If you use [`CredentialsProvider`](https://next-auth.js.org/providers/credentials ':target=blank'), i.e. email/password based auth, you can also use the `authorize` helper to implement how credentials are verified against the database: - -```ts -// pages/api/auth/[...nextauth].ts - -import service from '@zenstackhq/runtime/server'; -import { authorize } from '@zenstackhq/next-auth'; -import NextAuth, { type NextAuthOptions } from 'next-auth'; - -export const authOptions: NextAuthOptions = { - ... - providers: [ - CredentialsProvider({ - credentials: { - email: { - label: 'Email Address', - type: 'email', - }, - password: { - label: 'Password', - type: 'password', - }, - }, - - // use ZenStack's default implementation to verify credentials - authorize: authorize(service), - }), - ]}; - -export default NextAuth(authOptions); -``` - -### Configuring ZenStack services - -ZenStack's CRUD services need to be configured with a `getServerUser` callback for fetching current login user from the backend. This can be easily done when using Next-Auth's `unstable_getServerSession` API: - -```ts -// pages/api/zenstack/[...path].ts - -... -import service, { - type RequestHandlerOptions, - requestHandler, -} from '@zenstackhq/runtime/server'; -import { authOptions } from '../auth/[...nextauth]'; -import { unstable_getServerSession } from 'next-auth'; - -const options: RequestHandlerOptions = { - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - const session = await unstable_getServerSession(req, res, authOptions); - return session?.user; - }, -}; -export default requestHandler(service, options); - -``` - -_NOTE_ Although the name `unstable_getServerSession` looks suspicious, it's officially recommended by Next-Auth and is production-ready. - -### Data model requirement - -NextAuth is agnostic about the type of underlying database, but it requires certain table structures, depending on how you configure it. Your ZModel definitions should reflect these requirements. A sample `User` model is shown here (to be used with `CredentialsProvider`): - -```zmodel -model User { - id String @id @default(cuid()) - email String @unique @email - emailVerified DateTime? - password String @password @omit - name String? - image String? @url - - // open to signup - @@allow('create', true) - - // full access by oneself - @@allow('all', auth() == this) -} -``` - -You can find the detailed database model requirements [here](https://next-auth.js.org/adapters/models ':target=blank'). - -## Iron-session - -[Iron-session](https://www.npmjs.com/package/iron-session ':target=blank') is a lightweighted authentication toolkit. - -### Authentication endpoints - -Iron-session requires you to implement auth related API endpoints by yourself. Usually you need to at least have these three endpoints: **api/auth/login**, **/api/auth/logout**, and **/api/auth/user**. The following code shows how to use ZenStack backend service to implement them. - -- **/api/auth/login** - -```ts -... -import service from '@zenstackhq/runtime/server'; -import * as bcrypt from 'bcryptjs'; - -const loginRoute: NextApiHandler = async (req, res) => { - const { email, password } = req.body; - - const user = await service.db.user.findUnique({ where: { email } }); - if (!user || !bcrypt.compareSync(password, user.password)) { - res.status(401).json({ - message: 'invalid email and password combination', - }); - return; - } - - delete (user as any).password; - req.session.user = user; - await req.session.save(); - - res.json(user); -}; - -export default withIronSessionApiRoute(loginRoute, sessionOptions); -``` - -- **/api/auth/logout** - -```ts -... - -const logoutRoute: NextApiHandler = async (req, res) => { - req.session.destroy(); - res.json({}); -}; - -export default withIronSessionApiRoute(logoutRoute, sessionOptions); - -``` - -- **/api/auth/user** - -```ts -... -import service from '@zenstackhq/runtime/server'; - -const userRoute: NextApiHandler = async (req, res) => { - if (req.session?.user) { - // fetch user from db for fresh data - const user = await service.db.user.findUnique({ - where: { email: req.session.user.email }, - }); - if (!user) { - res.status(401).json({ message: 'invalid login status' }); - return; - } - - delete (user as any).password; - res.json(user); - } else { - res.status(401).json({ message: 'invalid login status' }); - } -}; - -export default withIronSessionApiRoute(userRoute, sessionOptions); -``` - -### Configuring ZenStack services - -ZenStack's CRUD services need to be configured with a `getServerUser` callback for fetching current login user from the backend. This can be easily done when using iron-session: - -```ts -// pages/api/zenstack/[...path].ts - -... -import service, { - requestHandler, - type RequestHandlerOptions, -} from '@zenstackhq/runtime/server'; - -const options: RequestHandlerOptions = { - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - const user = req.session?.user; - if (!user) { - return undefined; - } - - const dbUser = await service.db.user.findUnique({ - where: { email: user.email }, - }); - - return dbUser ?? undefined; - }, -}; - -export default withIronSessionApiRoute( - requestHandler(service, options), - sessionOptions -); -``` - -## Custom-built authentication - -[TBD] diff --git a/doc-serve/public/modeling-your-app.md b/doc-serve/public/modeling-your-app.md deleted file mode 100644 index 5cb860652..000000000 --- a/doc-serve/public/modeling-your-app.md +++ /dev/null @@ -1,168 +0,0 @@ -# Modeling your app - -ZenStack provides an integrated DSL called **ZModel** for defining your data models, relations, and access policies. It may sounds scary to learn yet another new language, but trust me is simple and intuitive. - -**ZModel** DSL is extended from the schema language of [Prisma ORM](https://www.prisma.io/docs/concepts/components/prisma-schema ':target=_blank'). Familarity of Prisma will make it very easy to start, but it's not a prerequisite. - -## Configuring data source - -The very first thing to do is to configure how to connect to your database. - -Here's an example for using a PosgreSQL with is connection string read from `DATABASE_URL` environment variable: - -```zmodel -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} -``` - -The generated CRUD services use the data source settings to connect to the database. Also, the migration workflow relies on it to synchronize database schema with the model. - -## Adding data models - -Data models define the shapes of business entities in your app. A data model consists of fields and attributes (which attach extra behavior to fields). - -Here's an example of a blog post model: - -```zmodel -model Post { - // @id attribute marks a field as unique identifier, - // mapped to database table's primary key - id String @id @default(cuid()) - - // fields can be DateTime - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - // or string - title String - - // or integer - viewCount Int @default(0) - - // and optional - content String? - - // and a list too - tags String[] -} -``` - -Check [here](zmodel-field.md) for more details about defining fields. - -## Adding relations - -An app is usually made up of a bunch of interconnected data models. You can define their relations with the special `@relation` attibute. - -Here are some examples: - -- One-to-one - -```zmodel -model User { - id String @id - profile Profile? -} - -model Profile { - id String @id - user @relation(fields: [userId], references: [id]) - userId String @unique -} -``` - -- One-to-many - -```zmodel -model User { - id String @id - posts Post[] -} - -model Post { - id String @id - author User? @relation(fields: [authorId], references: [id]) - authorId String? -} -``` - -- Many-to-many - -```zmodel -model Space { - id String @id - members Membership[] -} - -// Membership is the "join-model" between User and Space -model Membership { - id String @id() - - // one-to-many from Space - space Space @relation(fields: [spaceId], references: [id]) - spaceId String - - // one-to-many from User - user User @relation(fields: [userId], references: [id]) - userId String - - // a user can be member of a space for only once - @@unique([userId, spaceId]) -} - -model User { - id String @id - membership Membership[] -} -``` - -Check [here](zmodel-relation.md) for more details about defining relations. - -## Adding access policies - -It's great to see our app's business model is in place now, but it's still missing an important aspect: **access policy**, i.e., who can take what action to which data. - -Access policies are defined using `@@allow` and `@@deny` attributes. _NOTE_ attributes with `@@` prefix are to be used at model level. - -A few quick notes before diving into examples: - -- Access kinds include `create`, `read`, `update` and `delete`, and you can use `all` to abbreviate full grant. - -- By default, all access kinds are denied for a model. You can use arbitrary number of `@@allow` and `@@deny` rules in a model. See [here](zmodel-access-policy.md#combining-multiple-rules) for the semantic of combining them. - -- You can access current login user with the builtin `auth()` function. See [here](integrating-authentication.md) for how authentication is integrated. - -Let's look at a few examples now: - -```zmodel -model User { - id String @id - posts Post[] - ... - - // User can be created unconditionally (sign-up) - @@allow("create", true) -} - -model Post { - id String @id - author User @relation(fields: [authorId], references: [id]) - authorId String - published Boolean @default(false) - ... - - // deny all unauthenticated write access - @@deny("create,update,delete", auth() == null) - - // published posts can be read by all - @@allow("read", published) - - // grant full access to author - @@allow("all", auth() == author) -} -``` - -You can find more details about access policy [here](zmodel-access-policy.md). Also, check out the [Collaborative Todo App](https://github.com/zenstackhq/todo-demo-sqlite) sample for a more sophisticated policy design. - -Now you've got a fairly complete model for the app. Let's go ahead with [generating code](code-generation.md) from it then. diff --git a/doc-serve/public/quick-start.md b/doc-serve/public/quick-start.md deleted file mode 100644 index 3e71dc6fc..000000000 --- a/doc-serve/public/quick-start.md +++ /dev/null @@ -1,134 +0,0 @@ -# Quick start - -Please check out the corresponding guide for [creating a new project](#creating-a-new-project) or [adding to an existing project](#adding-to-an-existing-project). - -## Creating a new project - -You can choose from these preconfigured starter to create a new project: - -- [Using Next-Auth for authentication](#with-next-auth) -- [Using iron-session for authentication](#with-iron-session) -- [Without integrating with authentication](#without-integrating-authentication) - -### With Next-Auth - -Follow these steps to create a new project from a preconfigured template using [Next-Auth](https://next-auth.js.org/ ':target=blank') for authentication: - -1. Clone from starter template - -```bash -npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-auth-starter -``` - -2. Install dependencies - -```bash -npm install -``` - -3. Generate CRUD services and hooks code from the starter model - -```bash -npm run generate -``` - -4. push database schema to the local sqlite db - -```bash -npm run db:push -``` - -5. start dev server - -``` -npm run dev -``` - -### With iron-session - -Follow these steps to create a new project from a preconfigured template using [iron-session](https://www.npmjs.com/package/iron-session ':target=blank') for authentication: - -1. Clone from starter template - -```bash -npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-iron-session-starter -``` - -2. Install dependencies - -```bash -npm install -``` - -3. Generate CRUD services and hooks code from the starter model - -```bash -npm run generate -``` - -4. push database schema to the local sqlite db - -```bash -npm run db:push -``` - -5. start dev server - -``` -npm run dev -``` - -### Without integrating authentication - -If you would rather not use a template preconfigured with authentication, you can use the barebone starter instead. You can add an authentication solution later or hand-code it by yourself. - -1. Clone from starter template - -```bash -npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-barebone-starter -``` - -2. Install dependencies - -```bash -npm install -``` - -3. Generate CRUD services and hooks code from the starter model - -```bash -npm run generate -``` - -4. push database schema to the local sqlite db - -```bash -npm run db:push -``` - -5. start dev server - -``` -npm run dev -``` - -### Check result - -If everything worked, you should see a simple blog app like this: -![starter screen shot](_media/starter-shot.png 'Starter project screenshot') - -No worries if a blogger app doesn't suit you. The created project contains a starter model at `/zenstack/schema.zmodel`. You can modify it and build up your application's own model following [this guide](modeling-your-app.md). - -## Adding to an existing project - -To add ZenStack to an existing Next.js + Typescript project, run command below: - -```bash -npx zenstack init -``` - -You should find a `/zenstack/schema.model` file created, containing a simple blogger model in it. No worries if a blogger app doesn't suit you. You can modify it and build up your application's own model following [this guide](modeling-your-app.md). - -## Installing VSCode extension - -It's good idea to install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=zenstack.zenstack ':target=_blank') so you get syntax highlighting and error checking when authoring model files. diff --git a/doc-serve/public/reach-out.md b/doc-serve/public/reach-out.md deleted file mode 100644 index e57290e72..000000000 --- a/doc-serve/public/reach-out.md +++ /dev/null @@ -1,9 +0,0 @@ -# Reach out to the developers - -As developers of ZenStack, we hope this toolkit can assist you to build a cool app. -Should you have any questions or ideas, please feel free to reach out to us by any of the following methods. We'll be happy to help you out. - -- [Discord](https://go.zenstack.dev/chat) -- [GitHub Discussions](https://github.com/zenstackhq/zenstack/discussions) -- [Twitter](https://twitter.com/zenstackhq) -- Email us: [contact@zenstack.dev](mailto:contact@zenstack.dev) diff --git a/doc-serve/public/robots.txt b/doc-serve/public/robots.txt deleted file mode 100644 index c2a49f4fb..000000000 --- a/doc-serve/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Allow: / diff --git a/doc-serve/public/runtime-api.md b/doc-serve/public/runtime-api.md deleted file mode 100644 index 07d7b4833..000000000 --- a/doc-serve/public/runtime-api.md +++ /dev/null @@ -1,231 +0,0 @@ -# Runtime API - -## `@zenstackhq/runtime/types` - -This module contains types generated from ZModel data models. These types are shared by both the client-side and the server-side code. - -The generated types include (for each data model defined): - -- Entity type -- Data structure for creating/updating entities -- Data structure for selecting entities - including filtering and sorting - -Take `User` model as an example, here're some of the most commonly used types: - -- `User` - - The entity type which directly corresponds to the data mdoel. - -- `UserFindUniqueArgs` - - Argument type for finding a unique `User`. - -- `UserFindManyArgs` - - Argument type for finding a list of `User`s. - -- `UserCreateArgs` - - Argument for creating a new `User`. - -- `UserUpdateArgs` - - Argument for updating an existing `User`. - -## `@zenstackhq/runtime/client` - -This module contains API for client-side programming, including the generated React hooks and auxiliary types, like options and error types. - -_NOTE_ You should not import this module into server-side code, like getServerSideProps, or API endpoint. - -A `useXXX` API is generated fo each data model for getting the React hooks. The following code uses `User` model as an example. - -```ts -const { get, find, create, update, del } = useUser(); -``` - -### `RequestOptions` - -Options controlling hooks' fetch behavior. - -```ts -type RequestOptions = { - // indicates if fetch should be disabled - disabled?: boolean; - - // provides initial data, which is immediately available - // before fresh data is fetched (usually used with SSR) - initialData?: T; -}; -``` - -### `HooksError` - -Error thrown for failure of `create`, `update` and `delete` hooks. - -```ts -export type HooksError = { - status: number; - info: { - code: ServerErrorCode; - message: string; - }; -}; -``` - -#### `ServerErrorCode` - -| Code | Description | -| ------------------------------ | --------------------------------------------------------------------------------------------- | -| ENTITY_NOT_FOUND | The specified entity cannot be found | -| INVALID_REQUEST_PARAMS | The request parameter is invalid, either containing invalid fields or missing required fields | -| DENIED_BY_POLICY | The request is rejected by policy checks | -| UNIQUE_CONSTRAINT_VIOLATION | Violation of database unique constraints | -| REFERENCE_CONSTRAINT_VIOLATION | Violation of database reference constraint (aka. foreign key constraints) | -| READ_BACK_AFTER_WRITE_DENIED | A write operation succeeded but the result cannot be read back due to policy control | - -### `get` - -```ts -function get( - id: string | undefined, - args?: UserFindFirstArgs, - options?: RequestOptions -): SWRResponse; -``` - -### `find` - -```ts -function find( - args?: UserFindManyArgs, - options?: RequestOptions -): SWRResponse; -``` - -### `create` - -```ts -function create(args?: UserCreateArgs): Promise; -``` - -### `update` - -```ts -function update(id: string, args?: UserUpdateArgs): Promise; -``` - -### `del` - -```ts -function del(id: string, args?: UserDeleteArgs): Promise; -``` - -## `@zenstackhq/runtime/server` - -This module contains API for server-side programming. The following declarations are exported: - -### `service` - -The default export of this module is a `service` object which encapsulates most of the server-side APIs. - -#### Server-side CRUD - -The `service` object contains members for each of the data models, each containing server-side CRUD APIs. These APIs can be used for doing CRUD operations without HTTP request overhead, while still fully protected by access policies. - -The server-side CRUD APIs have similar signature with client-side hooks, except that they take an extra `queryContext` parameter for passing in the current login user. They're usually used for implementing SSR or custom API endpoints. - -- get - - ```ts - async get( - context: QueryContext, - id: string, - args?: UserFindFirstArgs - ): Promise; - ``` - -- find - - ```ts - async find( - context: QueryContext, - args?: UserFindManyArgs - ): Promise; - ``` - -- create - - ```ts - async create( - context: QueryContext, - args?: UserCreateArgs - ): Promise; - ``` - -- update - - ```ts - async update( - context: QueryContext, - id: string, - args?: UserUpdateArgs - ): Promise; - ``` - -- del - ```ts - async del( - context: QueryContext, - id: string, - args?: UserDeleteArgs - ): Promise; - ``` - -#### Direct database access - -The `service.db` object contains a member field for each data model defined, which you can use to conduct database operations for that model. - -_NOTE_ These database operations are **NOT** protected by access policies. - -Take `User` model for example: - -```ts -import service from '@zenstackhq/runtime/server'; - -// find all users -const users = service.db.user.find(); - -// update a user -await service.db.user.update({ - where: { id: userId }, - data: { email: newEmail }, -}); -``` - -The server-side database access API uses the [same set of typing](#zenstackhqruntimetypes) as the client side. The `service.db` object is a Prisma Client, and you can find all API documentations [here](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference ':target=blank'). - -### `requestHandler` - -Function for handling API endpoint requests. Used for installing the generated CRUD services onto an API route: - -```ts -// pages/api/zenstack/[...path].ts - -import service from '@zenstackhq/runtime'; -import { - requestHandler, - type RequestHandlerOptions, -} from '@zenstackhq/runtime/server'; -import { NextApiRequest, NextApiResponse } from 'next'; - -const options: RequestHandlerOptions = { - // a callback for getting the current login user - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - ... - }, -}; -export default requestHandler(service, options); -``` - -The `getServerUser` callback method is used for getting the current login user on the server side. Its implementation depends on how you authenticate users. diff --git a/doc-serve/public/server-side-rendering.md b/doc-serve/public/server-side-rendering.md deleted file mode 100644 index e2ce76829..000000000 --- a/doc-serve/public/server-side-rendering.md +++ /dev/null @@ -1,26 +0,0 @@ -# Server-side rendering - -You can use the `service` object to conduct CRUD operations on the server side directly without the overhead of HTTP requests. The `service` object contains members for each of the data model defined. - -The server-side CRUD methods are similar signature with client-side hooks, except that they take an extra `queryContext` parameter for passing in the current login user. Like client-side hooks, the CRUD operations are fully protected by access policies defined in ZModel. - -These methods are handy for implementing SSR (or custom API endpoints). Here's an example (using Next-Auth for authentication): - -```ts -import service from '@zenstackhq/runtime/server'; -import { unstable_getServerSession } from 'next-auth'; -... - -export const getServerSideProps = async ({ - req, - res, - params, -}) => { - const session = await unstable_getServerSession(req, res, authOptions); - const queryContext = { user: session?.user }; - const posts = await service.post.find(queryContext); - return { - props: { posts }, - }; -}; -``` diff --git a/doc-serve/public/setup-logging.md b/doc-serve/public/setup-logging.md deleted file mode 100644 index b3c2b4099..000000000 --- a/doc-serve/public/setup-logging.md +++ /dev/null @@ -1,92 +0,0 @@ -# Set up logging - -ZenStack uses the following levels to control server-side logging: - -- `error` - - Error level logging - -- `warn` - - Warning level logging - -- `info` - - Info level logging - -- `verbose` - - Verbose level logging - -- `query` - - Detailed database query logging - -By default, ZenStack prints `error` and `warn` level of logging with `console.error` and `console.log`, respectively. You can also control the logging behavior by providing a `zenstack.config.json` file at the root of your project. - -You can turn log levels on and off in `zenstack.config.json`: - -```json -{ - "log": ["verbose", "info"] -} -``` - -The settings shown above is an shorthand for: - -```json -{ - "log": [ - { - "level": "verbose", - "emit": "stdout" - }, - { - "level": "info", - "emit": "stdout" - } - ] -} -``` - -You can also configure ZenStack to emit log as event instead of dumping to stdout, like: - -```json -{ - "log": [ - { - "level": "info", - "emit": "event" - } - ] -} -``` - -To consume the events: - -```ts -import service from '@zenstackhq/runtime'; - -service.$on('info', (event) => { - console.log(event.timestamp, event.message); -}); -``` - -You can also mix and match stdout output with event emitting, like: - -```json -{ - "log": [ - { - "level": "info", - "emit": "stdout" - }, - { - "level": "info", - "emit": "event" - } - ] -} -``` - -The settings in `zenstack.config.json` controls logging of both ZenStack and the underlying Prisma instance. diff --git a/doc-serve/public/sitemap.xml b/doc-serve/public/sitemap.xml deleted file mode 100644 index c15f0c84a..000000000 --- a/doc-serve/public/sitemap.xml +++ /dev/null @@ -1 +0,0 @@ -https://zenstack.dev/dailyhttps://zenstack.dev/changelogdailyhttps://zenstack.dev/building-your-appdailyhttps://zenstack.dev/choosing-a-databasedailyhttps://zenstack.dev/cli-commandsdailyhttps://zenstack.dev/code-generationdailyhttps://zenstack.dev/entity-types-serverdailyhttps://zenstack.dev/entity-typesdailyhttps://zenstack.dev/evolving-model-with-migrationdailyhttps://zenstack.dev/integrating-authenticationdailyhttps://zenstack.dev/modeling-your-appdailyhttps://zenstack.dev/quick-startdailyhttps://zenstack.dev/reach-outdailyhttps://zenstack.dev/runtime-apidailyhttps://zenstack.dev/server-side-renderingdailyhttps://zenstack.dev/setup-loggingdailyhttps://zenstack.dev/telemetrydailyhttps://zenstack.dev/vscode-extensiondailyhttps://zenstack.dev/zmodel-access-policydailyhttps://zenstack.dev/zmodel-attributedailyhttps://zenstack.dev/zmodel-data-modeldailyhttps://zenstack.dev/zmodel-data-sourcedailyhttps://zenstack.dev/zmodel-enumdailyhttps://zenstack.dev/zmodel-field-constraintdailyhttps://zenstack.dev/zmodel-fielddailyhttps://zenstack.dev/zmodel-overviewdailyhttps://zenstack.dev/zmodel-referential-actiondailyhttps://zenstack.dev/zmodel-relationdaily \ No newline at end of file diff --git a/doc-serve/public/static/building-your-app.html b/doc-serve/public/static/building-your-app.html deleted file mode 100644 index 9a1acd551..000000000 --- a/doc-serve/public/static/building-your-app.html +++ /dev/null @@ -1,231 +0,0 @@ - - - Building your app - - - - - - - - - - - - - - - - - - - - - - - - - -

Building your app

The code generated from your model covers everything you need to implement CRUD, frontend and backend. This section illustrates the steps of using them when building your app.

Mounting backend services

First you should mount the generated server-side code as a Next.js API endpoint. Here's an example:

// pages/api/zenstack/[...path].ts
-
-import { authOptions } from '@api/auth/[...nextauth]';
-import service from '@zenstackhq/runtime';
-import {
-    requestHandler,
-    type RequestHandlerOptions,
-} from '@zenstackhq/runtime/server';
-import { NextApiRequest, NextApiResponse } from 'next';
-import { unstable_getServerSession } from 'next-auth';
-
-const options: RequestHandlerOptions = {
-    // a callback for getting the current login user
-    async getServerUser(req: NextApiRequest, res: NextApiResponse) {
-        // here we use NextAuth is used as an example, and you can change it to
-        // suit the authentication solution you use
-        const session = await unstable_getServerSession(req, res, authOptions);
-        return session?.user;
-    },
-};
-export default requestHandler(service, options);

Please note that the services need to be configured with a callback getServerUser for getting the current login user. The example above uses NextAuth to do it, but you can also hand-code it based on the authentication approach you use, as long as it returns a user object that represents the current session's user.

NOTE Check out this guide for more details about integrating with authentication.

Make sure the services are mounted at route /api/zenstack/ with a catch all parameter named path, as this is required by the generate React hooks.

optional Integrating with NextAuth

If you use NextAuth for authentication, you can install the @zenstackhq/next-auth package for easier integration. This package provides an adapter and authorization helper that you can use to configure NextAuth.

// pages/api/auth/[...nextauth].ts
-
-import service from '@zenstackhq/runtime';
-import { authorize, Adapter } from '@zenstackhq/next-auth';
-import NextAuth, { type NextAuthOptions } from 'next-auth';
-
-export const authOptions: NextAuthOptions = {
-    // use ZenStack adapter for persistence
-    adapter: Adapter(service),
-
-    providers: [
-        CredentialsProvider({
-            credentials: { ... },
-            // use the generated "authorize" helper for credential based authentication
-            authorize: authorize(service),
-        }),
-    ]
-
-    ...
-};
-
-export default NextAuth(authOptions);

Find more details here about integrating with different authentication schemes.

Using React hooks

React hooks are generated for CRUD'ing each data model you defined. They save your time writing explicit HTTP requests to call the generated services. Internally the hooks use SWR for data fetching, so you'll also enjoy its built-in features, like caching, revalidation on interval, etc.

NOTE The generated service code is injected with the access policies you defined in the model, so it's already secure, regardless called directly or via hooks. A read operation only returns data that's supposed to be visible to the current user, and a write operation is rejected if the policies verdict so.

Read

Call find and get hooks for listing entities or loading a specific one. If your entity has relations, you can request related entities to be loaded together.

const { find } = usePost();
-// lists unpublished posts with their author's data
-const posts = find({
-    where: { published: false },
-    include: { author: true },
-    orderBy: { updatedAt: 'desc' },
-});
const { get } = usePost();
-// fetches a post with its author's data
-const post = get(id, {
-    include: { author: true },
-});

Create

Call the async create method to create a new model entity. Note that if the model has relations, you can create related entities in a nested write. See example below:

const { create } = usePost();
-// creating a new post for current user with a nested comment
-const post = await create({
-    data: {
-        title: 'My New Post',
-        author: {
-            connect: { id: session.user.id },
-        },
-        comments: {
-            create: [{ content: 'First comment' }],
-        },
-    },
-});

Update

Similar to create, the update hook also allows nested write.

const { update } = usePost();
-// updating a post's content and create a new comment
-const post = await update(id, {
-    data: {
-        const: 'My post content',
-        comments: {
-            create: [{ content: 'A new comment' }],
-        },
-    },
-});

Delete

const { del } = usePost();
-const post = await del(id);

Server-side coding

Since doing CRUD with hooks is already secure, in many cases, you can implement your business logic right in the frontend code.

ZenStack also supports server-side programming for conducting CRUD without sending HTTP requests, or even direct database access (bypassing access policy checks). Please check the following documentation for details:

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/changelog.html b/doc-serve/public/static/changelog.html deleted file mode 100644 index 86e659943..000000000 --- a/doc-serve/public/static/changelog.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Changelog - - - - - - - - - - - - - - - - - - - - - - - - - -

0.4.0 (2022-12-01)

Features

  • zenstack init command for initializing a project, #109, doc.

  • Field constraint suport, #94, doc.

  • Support for server-side CRUD with access policy check (SSR), #126, doc.

  • Options for disabling fetching in hooks (useful when arguments are not ready), #57, doc.

  • Telemetry in CLI, #102, doc.

  • Iron-session based starter, #95, link.

  • Barebone starter (without authentication), link.

  • Website is live!

Fixes and improvements

  • Merge @zenstackhq/internal into @zenstackhq/runtime so as to have a single runtime dependency, #70.

  • More accurate log for access policy violation, #71.

  • auth() function's return type is now resolved to User model in ZModel, instead of Any, #65.

  • Improved ZModel type checking, #67, #46, #99.

  • Upgraded to Prisma 4.7.

Breaking changes

  • @zenstackhq/runtime doesn't export anything now.

    Use @zenstackhq/runtime/types for type definitions shared between client and server, @zenstackhq/runtime/client for client-specific libaries (like React hooks), and @zenstackhq/runtime/server for server-specific libraries.

0.3.0 (2022-11-08)

Features

  • @password and @omit attribute support

  • Configurable logging (to stdout and emitting as events)

Fixes and improvements

  • More robust policy checks

  • Properly handles complex types like BigInt, Date, Decimal, etc.

  • Makes sure Prisma schema is regenerated for related CLI commands

  • Lower VSCode engine version requirement for the extension

  • Better overall documentation

0.2.0 (2022-10-29)

Features

  • ZModel data modeling schema (an extension to Prisma Schema)

  • zenstack cli for generating RESTful services, auth adapters and React hooks from ZModel

  • Policy engine that transforms policy rules into Prisma query conditions

  • Runtime packages

  • An initial set of tests

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/choosing-a-database.html b/doc-serve/public/static/choosing-a-database.html deleted file mode 100644 index a0010cf5d..000000000 --- a/doc-serve/public/static/choosing-a-database.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Choosing a database - - - - - - - - - - - - - - - - - - - - - - - - - -

Choosing a database

ZenStack is agnostic about where and how you deploy your web app, but hosting on serverless platforms like Vercel is definitely a popular choice.

Serverless architecture has some implications on how you should care about your database hosting. Different from traditional architecture where you have a fixed number of long-running Node.js servers, in a serverless environment, a new Node.js context can potentially be created for each user request, and if traffic volume is high, this can quickly exhaust your database's connection limit, if you connect to the database directly without a proxy.

You'll likely be OK if your app has a low number of concurrent users, otherwise you should consider using a proxy in front of your database server. Here's a number of (incomplete) solutions you can consider:

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/cli-commands.html b/doc-serve/public/static/cli-commands.html deleted file mode 100644 index 512bd925d..000000000 --- a/doc-serve/public/static/cli-commands.html +++ /dev/null @@ -1,164 +0,0 @@ - - - Commands - - - - - - - - - - - - - - - - - - - - - - - - - -

CLI commands

init

Set up ZenStack for an existing Next.js + Typescript project.

npx zenstack init [options] [dir]

Options:

    -p, --package-manager <pm>: package manager to use: "npm", "yarn", or "pnpm" (default: auto detect)

generate

Generates RESTful CRUD API and React hooks from your model.

npx zenstack generate [options]

Options:

    --schema <file>: schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")
-
-    -p, --package-manager <pm>: package manager to use: "npm", "yarn", or "pnpm" (default: auto detect)

migrate

Update the database schema with migrations.

Sub-commands:

migrate dev

Create a migration from changes in Prisma schema, apply it to the database, trigger generation of database client. This command wraps prisma migrate command.

npx zenstack migrate dev [options]

Options:

    --schema <file> schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")

migrate reset

Reset your database and apply all migrations.

npx zenstack migrate reset [options]

Options:

    --schema <file> schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")

migrate deploy

Apply pending migrations to the database in production/staging.

npx zenstack migrate deploy [options]

Options:

    --schema <file> schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")

migrate status

Check the status of migrations in the production/staging database.

npx zenstack migrate status [options]

Options:

    --schema <file> schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")

db

Manage your database schema and lifecycle during development. This command wraps prisma db command.

Sub-commands:

db push

Push the state from model to the database during prototyping.

npx zenstack db push [options]

Options:

    --schema <file> schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")
-    --accept-data-loss  Ignore data loss warnings

studio

Browse your data with Prisma Studio. This command wraps prisma studio command.

npx zenstack studio [options]

Options:

    --schema <file>         schema file (with extension .zmodel) (default: "./zenstack/schema.zmodel")
-    -p --port <port>        Port to start Studio in
-    -b --browser <browser>  Browser to open Studio in
-    -n --hostname           Hostname to bind the Express server to
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/code-generation.html b/doc-serve/public/static/code-generation.html deleted file mode 100644 index 779616d17..000000000 --- a/doc-serve/public/static/code-generation.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Code generation - - - - - - - - - - - - - - - - - - - - - - - - - -

Code generation

Code generation is where your modeling work pays off. To trigger it, simply run:

npx zenstack generate

You should see an output similar to:

CLI screenshot

A full reference of the zenstack CLI can be found here, but for now knowing just this one command is good enough.

As you see, several code generators are run to create pieces of code that help you build the app. Let's see how to use it in the next section.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/entity-types-server.html b/doc-serve/public/static/entity-types-server.html deleted file mode 100644 index 957f4b3a7..000000000 --- a/doc-serve/public/static/entity-types-server.html +++ /dev/null @@ -1,205 +0,0 @@ - - - ZenStack - toolkit for building CRUD apps with Next.js + Typescript - - - - - - - - - - - - - - - - - - - - - - - - - -

Types

Module @zenstackhq/runtime/types contains type definitions of entities, filters, sorting, etc., generated from ZModel data models. The types can be used in both the front-end and the backend code.

Suppose a User model is defined in ZModel:

model User {
-    id String @id @default(cuid())
-    email String @unique @email
-    password String @password @omit
-    name String?
-    posts Post[]
-}

The following types are generated:

Entity type

export type User = {
-  id: string
-  email: string
-  password: string | null
-  name: string | null
-  posts: Post[]
-}```
-
-This type serves as the return type of the generated React hooks:
-
-```ts
-import { type User } from '@zenstackhq/runtime/types';
-import { useUser } from '@zenstackhq/runtime/client';
-
-export function MyComponent() {
-    const { find } = useUser();
-    const result = find();
-    const users: User[] = result.data;
-    ...
-}

Backend database access API also returns the same type:

const users: User[] = await service.db.user.find();

Filter and sort type

Types for filtering and sorting entites are also generated:

export type UserFindManyArgs = {
-    select?: UserSelect | null;
-    include?: UserInclude | null;
-    where?: UserWhereInput;
-    orderBy?: Enumerable<UserOrderByWithRelationInput>;
-    ...
-};

You can use it like:

const { find } = useUser();
-const { data: users } = find({
-    where: {
-        email: {
-            endsWith: '@zenstack.dev',
-        },
-    },
-    orderBy: [
-        {
-            email: 'asc',
-        },
-    ],
-    include: {
-        // include related Post entities
-        posts: true,
-    },
-});
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/entity-types.html b/doc-serve/public/static/entity-types.html deleted file mode 100644 index 4deb7b333..000000000 --- a/doc-serve/public/static/entity-types.html +++ /dev/null @@ -1,205 +0,0 @@ - - - ZenStack - toolkit for building CRUD apps with Next.js + Typescript - - - - - - - - - - - - - - - - - - - - - - - - - -

Types

Module @zenstackhq/runtime/types contains type definitions of entities, filters, sorting, etc., generated from ZModel data models. The types can be used in both the front-end and the backend code.

Suppose a User model is defined in ZModel:

model User {
-    id String @id @default(cuid())
-    email String @unique @email
-    password String @password @omit
-    name String?
-    posts Post[]
-}

The following types are generated:

Entity type

export type User = {
-  id: string
-  email: string
-  password: string | null
-  name: string | null
-  posts: Post[]
-}```
-
-This type serves as the return type of the generated React hooks:
-
-```ts
-import { type User } from '@zenstackhq/runtime/types';
-import { useUser } from '@zenstackhq/runtime/client';
-
-export function MyComponent() {
-    const { find } = useUser();
-    const result = find();
-    const users: User[] = result.data;
-    ...
-}

Backend database access API also returns the same type:

const users: User[] = await service.db.user.find();

Filter and sort type

Types for filtering and sorting entites are also generated:

export type UserFindManyArgs = {
-    select?: UserSelect | null;
-    include?: UserInclude | null;
-    where?: UserWhereInput;
-    orderBy?: Enumerable<UserOrderByWithRelationInput>;
-    ...
-};

You can use it like:

const { find } = useUser();
-const { data: users } = find({
-    where: {
-        email: {
-            endsWith: '@zenstack.dev',
-        },
-    },
-    orderBy: [
-        {
-            email: 'asc',
-        },
-    ],
-    include: {
-        // include related Post entities
-        posts: true,
-    },
-});
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/evolving-model-with-migration.html b/doc-serve/public/static/evolving-model-with-migration.html deleted file mode 100644 index 6fbc8ae63..000000000 --- a/doc-serve/public/static/evolving-model-with-migration.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Evolving model with migration - - - - - - - - - - - - - - - - - - - - - - - - - -

Evolving model with migration

When using ZenStack, your schema.zmodel file represents the current status of your app's data model and your database's schema. When you make changes to schema.zmodel, however, your data model drifts away from database schema. At your app's deployment time, such drift needs to be "fixed", and so that your database schema stays synchronized with your data model. This processing of "fixing" is called migration.

Here we summarize a few common scenarios and show how you should work on migration.

For a newly created schema.zmodel

When you're just starting out a ZenStack project, you have an empty migration history. After creating schema.zmodel, adding a development datasource and adding some models, run the following command to bootstrap your migration history and synchronize your development database schema:

npx zenstack migrate dev -n init

After it's run, you should find a folder named migrations created under zenstack folder, inside of which you can find a .sql file containing script that initializes your database. Please note that when you run "migration dev", the generated migration script is automatically run agains your datasource specified in schema.zmodel.

Make sure you commit the migrations folder into source control.

After updating an existing schema.zmodel

After making update to schema.zmodel, run the "migrate dev" command to generate an incremental migration record:

npx zenstack migrate dev -n [short-name-for-the-change]

If any database schema change is needed based on the previous version of data model, a new .sql file will be generated under zenstack/migrations folder. Your development database's schema is automatically synchronized after running the command.

Make sure you review that the generated .sql script reflects your intention before committing it to source control.

Pushing model changes to database without creating migration

This is helpful when you're prototyping locally and don't want to create migration records. Simply run:

npx zenstack db push

, and your database schema will be synced with schema.zmodel. After prototyping, reset your local database and generate migration records:

npx zenstack migrate reset
npx zenstack migrate dev -n [name]

During deployment

When deploying your app to an official environment (a shared dev environment, staging, or production), DO NOT run migrate dev command in CI scripts. Instead, run migrate deploy.

npx zenstack migrate deploy

The migrate deploy command does not generate new migration records. It simply detects records that are created after the previous deployment and execute them in order. As a result, your database schema is synchronized with data model.

If you've always been taking the "migrate dev" and "migrate deploy" loop during development, your migration should run smoothly. However manually changing db schema, manually changing/deleting migration records can result in failure during migration. Please refer to this documentation for troubleshooting migration issues in production.

Summary

ZenStack is built over Prisma and it internally delegates all ORM tasks to Prisma. The migration workflow is exactly the same as Prisma's workflow, with the only exception that the source of input is schema.zmodel, and a Prisma schema is generated on the fly. The set of migration commands that ZModel CLI offers, like "migrate dev" and "migrate deploy", are simple wrappers around Prisma commands.

Prisma has excellent documentation about migration. Make sure you look into those for a more thorough understanding.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/index.html b/doc-serve/public/static/index.html deleted file mode 100644 index 271af7497..000000000 --- a/doc-serve/public/static/index.html +++ /dev/null @@ -1,163 +0,0 @@ - - - ZenStack - toolkit for building CRUD apps with Next.js + Typescript - - - - - - - - - - - - - - - - - - - - - - - - - -

cover-logo

ZenStack 0.4.0

-

A toolkit for building secure CRUD apps with Next.js + Typescript.

-
  • Full-stack toolkit made for front-end developers
  • Intuitive and flexible data modeling
  • No more boilerplate CRUD code

GitHub -Get Started

ZenStack

-

A toolkit for building secure CRUD apps with Next.js.

-

What it is

ZenStack is a schema-first toolkit for defining data models, relations and access policies. It generates database schema, backend CRUD services and frontend React hooks for you automatically from the model. Our goal is to let you save time writing boilerplate code and focus on building real features!

NOTE: ZenStack is built above Prisma ORM - the greatest ORM solution for Typescript. It extends Prisma's power from database handling to full-stack development.

See the Quick start guide for more details.

Features

Examples

Check out the Collaborative Todo App for a running example. You can find the source code here.

Community

Join our discord server for chat and updates!

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/integrating-authentication.html b/doc-serve/public/static/integrating-authentication.html deleted file mode 100644 index 799bc300b..000000000 --- a/doc-serve/public/static/integrating-authentication.html +++ /dev/null @@ -1,303 +0,0 @@ - - - Integrating authentication - - - - - - - - - - - - - - - - - - - - - - - - - -

Integrating authentication

This documentation explains how to integrate ZenStack with popular authentication frameworks.

NextAuth

NextAuth is a comprehensive framework for implementating authentication. It offers a pluggable mechanism for configuring how user data is persisted. You can find a full example using ZenStack with NextAuth here.

ZenStack provides an extension package @zenstackhq/next-auth for integrating with NextAuth, which includes:

Adapter

Adapter is a NextAuth mechanism for hooking in custom persistence of auth related entities, like User, Account, etc. The ZenStack adapter can be configured to NextAuth as follows:

// pages/api/auth/[...nextauth].ts
-
-import service from '@zenstackhq/runtime/server';
-import { Adapter } from '@zenstackhq/next-auth';
-import NextAuth, { type NextAuthOptions } from 'next-auth';
-
-export const authOptions: NextAuthOptions = {
-    // install ZenStack adapter
-    adapter: Adapter(service),
-    ...
-};
-
-export default NextAuth(authOptions);

authorize helper

If you use CredentialsProvider, i.e. email/password based auth, you can also use the authorize helper to implement how credentials are verified against the database:

// pages/api/auth/[...nextauth].ts
-
-import service from '@zenstackhq/runtime/server';
-import { authorize } from '@zenstackhq/next-auth';
-import NextAuth, { type NextAuthOptions } from 'next-auth';
-
-export const authOptions: NextAuthOptions = {
-    ...
-    providers: [
-        CredentialsProvider({
-            credentials: {
-                email: {
-                    label: 'Email Address',
-                    type: 'email',
-                },
-                password: {
-                    label: 'Password',
-                    type: 'password',
-                },
-            },
-
-            // use ZenStack's default implementation to verify credentials
-            authorize: authorize(service),
-        }),
-    ]};
-
-export default NextAuth(authOptions);

Configuring ZenStack services

ZenStack's CRUD services need to be configured with a getServerUser callback for fetching current login user from the backend. This can be easily done when using Next-Auth's unstable_getServerSession API:

// pages/api/zenstack/[...path].ts
-
-...
-import service, {
-    type RequestHandlerOptions,
-    requestHandler,
-} from '@zenstackhq/runtime/server';
-import { authOptions } from '../auth/[...nextauth]';
-import { unstable_getServerSession } from 'next-auth';
-
-const options: RequestHandlerOptions = {
-    async getServerUser(req: NextApiRequest, res: NextApiResponse) {
-        const session = await unstable_getServerSession(req, res, authOptions);
-        return session?.user;
-    },
-};
-export default requestHandler(service, options);
-

NOTE Although the name unstable_getServerSession looks suspicious, it's officially recommended by Next-Auth and is production-ready.

Data model requirement

NextAuth is agnostic about the type of underlying database, but it requires certain table structures, depending on how you configure it. Your ZModel definitions should reflect these requirements. A sample User model is shown here (to be used with CredentialsProvider):

model User {
-    id String @id @default(cuid())
-    email String @unique @email
-    emailVerified DateTime?
-    password String @password @omit
-    name String?
-    image String? @url
-
-    // open to signup
-    @@allow('create', true)
-
-    // full access by oneself
-    @@allow('all', auth() == this)
-}

You can find the detailed database model requirements here.

Iron-session

Iron-session is a lightweighted authentication toolkit.

Authentication endpoints

Iron-session requires you to implement auth related API endpoints by yourself. Usually you need to at least have these three endpoints: api/auth/login, /api/auth/logout, and /api/auth/user. The following code shows how to use ZenStack backend service to implement them.

  • /api/auth/login
...
-import service from '@zenstackhq/runtime/server';
-import * as bcrypt from 'bcryptjs';
-
-const loginRoute: NextApiHandler = async (req, res) => {
-    const { email, password } = req.body;
-
-    const user = await service.db.user.findUnique({ where: { email } });
-    if (!user || !bcrypt.compareSync(password, user.password)) {
-        res.status(401).json({
-            message: 'invalid email and password combination',
-        });
-        return;
-    }
-
-    delete (user as any).password;
-    req.session.user = user;
-    await req.session.save();
-
-    res.json(user);
-};
-
-export default withIronSessionApiRoute(loginRoute, sessionOptions);
  • /api/auth/logout
...
-
-const logoutRoute: NextApiHandler = async (req, res) => {
-    req.session.destroy();
-    res.json({});
-};
-
-export default withIronSessionApiRoute(logoutRoute, sessionOptions);
-
  • /api/auth/user
...
-import service from '@zenstackhq/runtime/server';
-
-const userRoute: NextApiHandler<AuthResponseType> = async (req, res) => {
-    if (req.session?.user) {
-        // fetch user from db for fresh data
-        const user = await service.db.user.findUnique({
-            where: { email: req.session.user.email },
-        });
-        if (!user) {
-            res.status(401).json({ message: 'invalid login status' });
-            return;
-        }
-
-        delete (user as any).password;
-        res.json(user);
-    } else {
-        res.status(401).json({ message: 'invalid login status' });
-    }
-};
-
-export default withIronSessionApiRoute(userRoute, sessionOptions);

Configuring ZenStack services

ZenStack's CRUD services need to be configured with a getServerUser callback for fetching current login user from the backend. This can be easily done when using iron-session:

// pages/api/zenstack/[...path].ts
-
-...
-import service, {
-    requestHandler,
-    type RequestHandlerOptions,
-} from '@zenstackhq/runtime/server';
-
-const options: RequestHandlerOptions = {
-    async getServerUser(req: NextApiRequest, res: NextApiResponse) {
-        const user = req.session?.user;
-        if (!user) {
-            return undefined;
-        }
-
-        const dbUser = await service.db.user.findUnique({
-            where: { email: user.email },
-        });
-
-        return dbUser ?? undefined;
-    },
-};
-
-export default withIronSessionApiRoute(
-    requestHandler(service, options),
-    sessionOptions
-);

Custom-built authentication

[TBD]

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/modeling-your-app.html b/doc-serve/public/static/modeling-your-app.html deleted file mode 100644 index 41b82b237..000000000 --- a/doc-serve/public/static/modeling-your-app.html +++ /dev/null @@ -1,247 +0,0 @@ - - - Modeling your app - - - - - - - - - - - - - - - - - - - - - - - - - -

Modeling your app

ZenStack provides an integrated DSL called ZModel for defining your data models, relations, and access policies. It may sounds scary to learn yet another new language, but trust me is simple and intuitive.

ZModel DSL is extended from the schema language of Prisma ORM. Familarity of Prisma will make it very easy to start, but it's not a prerequisite.

Configuring data source

The very first thing to do is to configure how to connect to your database.

Here's an example for using a PosgreSQL with is connection string read from DATABASE_URL environment variable:

datasource db {
-    provider = "postgresql"
-    url = env("DATABASE_URL")
-}

The generated CRUD services use the data source settings to connect to the database. Also, the migration workflow relies on it to synchronize database schema with the model.

Adding data models

Data models define the shapes of business entities in your app. A data model consists of fields and attributes (which attach extra behavior to fields).

Here's an example of a blog post model:

model Post {
-    // @id attribute marks a field as unique identifier,
-    // mapped to database table's primary key
-    id String @id @default(cuid())
-
-    // fields can be DateTime
-    createdAt DateTime @default(now())
-    updatedAt DateTime @updatedAt
-
-    // or string
-    title String
-
-    // or integer
-    viewCount Int @default(0)
-
-    // and optional
-    content String?
-
-    // and a list too
-    tags String[]
-}

Check here for more details about defining fields.

Adding relations

An app is usually made up of a bunch of interconnected data models. You can define their relations with the special @relation attibute.

Here are some examples:

  • One-to-one
model User {
-    id String @id
-    profile Profile?
-}
-
-model Profile {
-    id String @id
-    user @relation(fields: [userId], references: [id])
-    userId String @unique
-}
  • One-to-many
model User {
-    id String @id
-    posts Post[]
-}
-
-model Post {
-    id String @id
-    author User? @relation(fields: [authorId], references: [id])
-    authorId String?
-}
  • Many-to-many
model Space {
-    id String @id
-    members Membership[]
-}
-
-// Membership is the "join-model" between User and Space
-model Membership {
-    id String @id()
-
-    // one-to-many from Space
-    space Space @relation(fields: [spaceId], references: [id])
-    spaceId String
-
-    // one-to-many from User
-    user User @relation(fields: [userId], references: [id])
-    userId String
-
-    // a user can be member of a space for only once
-    @@unique([userId, spaceId])
-}
-
-model User {
-    id String @id
-    membership Membership[]
-}

Check here for more details about defining relations.

Adding access policies

It's great to see our app's business model is in place now, but it's still missing an important aspect: access policy, i.e., who can take what action to which data.

Access policies are defined using @@allow and @@deny attributes. NOTE attributes with @@ prefix are to be used at model level.

A few quick notes before diving into examples:

  • Access kinds include create, read, update and delete, and you can use all to abbreviate full grant.

  • By default, all access kinds are denied for a model. You can use arbitrary number of @@allow and @@deny rules in a model. See here for the semantic of combining them.

  • You can access current login user with the builtin auth() function. See here for how authentication is integrated.

Let's look at a few examples now:

model User {
-    id String @id
-    posts Post[]
-    ...
-
-    // User can be created unconditionally (sign-up)
-    @@allow("create", true)
-}
-
-model Post {
-    id String @id
-    author User @relation(fields: [authorId], references: [id])
-    authorId String
-    published Boolean @default(false)
-    ...
-
-    // deny all unauthenticated write access
-    @@deny("create,update,delete", auth() == null)
-
-    // published posts can be read by all
-    @@allow("read", published)
-
-    // grant full access to author
-    @@allow("all", auth() == author)
-}

You can find more details about access policy here. Also, check out the Collaborative Todo App sample for a more sophisticated policy design.

Now you've got a fairly complete model for the app. Let's go ahead with generating code from it then.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/quick-start.html b/doc-serve/public/static/quick-start.html deleted file mode 100644 index 3a323afaf..000000000 --- a/doc-serve/public/static/quick-start.html +++ /dev/null @@ -1,159 +0,0 @@ - - - Quick start - - - - - - - - - - - - - - - - - - - - - - - - - -

Quick start

Please check out the corresponding guide for creating a new project or adding to an existing project.

Creating a new project

You can choose from these preconfigured starter to create a new project:

With Next-Auth

Follow these steps to create a new project from a preconfigured template using Next-Auth for authentication:

  1. Clone from starter template
npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-auth-starter
  1. Install dependencies
npm install
  1. Generate CRUD services and hooks code from the starter model
npm run generate
  1. push database schema to the local sqlite db
npm run db:push
  1. start dev server
npm run dev

With iron-session

Follow these steps to create a new project from a preconfigured template using iron-session for authentication:

  1. Clone from starter template
npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-iron-session-starter
  1. Install dependencies
npm install
  1. Generate CRUD services and hooks code from the starter model
npm run generate
  1. push database schema to the local sqlite db
npm run db:push
  1. start dev server
npm run dev

Without integrating authentication

If you would rather not use a template preconfigured with authentication, you can use the barebone starter instead. You can add an authentication solution later or hand-code it by yourself.

  1. Clone from starter template
npx create-next-app --use-npm -e https://github.com/zenstackhq/nextjs-barebone-starter
  1. Install dependencies
npm install
  1. Generate CRUD services and hooks code from the starter model
npm run generate
  1. push database schema to the local sqlite db
npm run db:push
  1. start dev server
npm run dev

Check result

If everything worked, you should see a simple blog app like this: -starter screen shot

No worries if a blogger app doesn't suit you. The created project contains a starter model at /zenstack/schema.zmodel. You can modify it and build up your application's own model following this guide.

Adding to an existing project

To add ZenStack to an existing Next.js + Typescript project, run command below:

npx zenstack init

You should find a /zenstack/schema.model file created, containing a simple blogger model in it. No worries if a blogger app doesn't suit you. You can modify it and build up your application's own model following this guide.

Installing VSCode extension

It's good idea to install the VSCode extension so you get syntax highlighting and error checking when authoring model files.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/reach-out.html b/doc-serve/public/static/reach-out.html deleted file mode 100644 index eaac22d83..000000000 --- a/doc-serve/public/static/reach-out.html +++ /dev/null @@ -1,159 +0,0 @@ - - - Reach out to the developers - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/runtime-api.html b/doc-serve/public/static/runtime-api.html deleted file mode 100644 index 7482fcca3..000000000 --- a/doc-serve/public/static/runtime-api.html +++ /dev/null @@ -1,252 +0,0 @@ - - - Runtime API - - - - - - - - - - - - - - - - - - - - - - - - - -

Runtime API

@zenstackhq/runtime/types

This module contains types generated from ZModel data models. These types are shared by both the client-side and the server-side code.

The generated types include (for each data model defined):

  • Entity type
  • Data structure for creating/updating entities
  • Data structure for selecting entities - including filtering and sorting

Take User model as an example, here're some of the most commonly used types:

  • User

    The entity type which directly corresponds to the data mdoel.

  • UserFindUniqueArgs

    Argument type for finding a unique User.

  • UserFindManyArgs

    Argument type for finding a list of Users.

  • UserCreateArgs

    Argument for creating a new User.

  • UserUpdateArgs

    Argument for updating an existing User.

@zenstackhq/runtime/client

This module contains API for client-side programming, including the generated React hooks and auxiliary types, like options and error types.

NOTE You should not import this module into server-side code, like getServerSideProps, or API endpoint.

A useXXX API is generated fo each data model for getting the React hooks. The following code uses User model as an example.

const { get, find, create, update, del } = useUser();

RequestOptions

Options controlling hooks' fetch behavior.

type RequestOptions<T> = {
-    // indicates if fetch should be disabled
-    disabled?: boolean;
-
-    // provides initial data, which is immediately available
-    // before fresh data is fetched (usually used with SSR)
-    initialData?: T;
-};

HooksError

Error thrown for failure of create, update and delete hooks.

export type HooksError = {
-    status: number;
-    info: {
-        code: ServerErrorCode;
-        message: string;
-    };
-};

ServerErrorCode

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CodeDescription
ENTITY_NOT_FOUNDThe specified entity cannot be found
INVALID_REQUEST_PARAMSThe request parameter is invalid, either containing invalid fields or missing required fields
DENIED_BY_POLICYThe request is rejected by policy checks
UNIQUE_CONSTRAINT_VIOLATIONViolation of database unique constraints
REFERENCE_CONSTRAINT_VIOLATIONViolation of database reference constraint (aka. foreign key constraints)
READ_BACK_AFTER_WRITE_DENIEDA write operation succeeded but the result cannot be read back due to policy control
-

get

function get(
-    id: string | undefined,
-    args?: UserFindFirstArgs,
-    options?: RequestOptions
-): SWRResponse<User>;

find

function find(
-    args?: UserFindManyArgs,
-    options?: RequestOptions
-): SWRResponse<User[]>;

create

function create(args?: UserCreateArgs): Promise<User | undefined>;

update

function update(id: string, args?: UserUpdateArgs): Promise<User | undefined>;

del

function del(id: string, args?: UserDeleteArgs): Promise<User | undefined>;

@zenstackhq/runtime/server

This module contains API for server-side programming. The following declarations are exported:

service

The default export of this module is a service object which encapsulates most of the server-side APIs.

Server-side CRUD

The service object contains members for each of the data models, each containing server-side CRUD APIs. These APIs can be used for doing CRUD operations without HTTP request overhead, while still fully protected by access policies.

The server-side CRUD APIs have similar signature with client-side hooks, except that they take an extra queryContext parameter for passing in the current login user. They're usually used for implementing SSR or custom API endpoints.

  • get

    async get(
    -    context: QueryContext,
    -    id: string,
    -    args?: UserFindFirstArgs
    -): Promise<User | undefined>;
  • find

    async find(
    -    context: QueryContext,
    -    args?: UserFindManyArgs
    -): Promise<User[]>;
  • create

    async create(
    -    context: QueryContext,
    -    args?: UserCreateArgs
    -): Promise<User>;
  • update

    async update(
    -    context: QueryContext,
    -    id: string,
    -    args?: UserUpdateArgs
    -): Promise<User>;
  • del

    async del(
    -    context: QueryContext,
    -    id: string,
    -    args?: UserDeleteArgs
    -): Promise<User>;

Direct database access

The service.db object contains a member field for each data model defined, which you can use to conduct database operations for that model.

NOTE These database operations are NOT protected by access policies.

Take User model for example:

import service from '@zenstackhq/runtime/server';
-
-// find all users
-const users = service.db.user.find();
-
-// update a user
-await service.db.user.update({
-    where: { id: userId },
-    data: { email: newEmail },
-});

The server-side database access API uses the same set of typing as the client side. The service.db object is a Prisma Client, and you can find all API documentations here.

requestHandler

Function for handling API endpoint requests. Used for installing the generated CRUD services onto an API route:

// pages/api/zenstack/[...path].ts
-
-import service from '@zenstackhq/runtime';
-import {
-    requestHandler,
-    type RequestHandlerOptions,
-} from '@zenstackhq/runtime/server';
-import { NextApiRequest, NextApiResponse } from 'next';
-
-const options: RequestHandlerOptions = {
-    // a callback for getting the current login user
-    async getServerUser(req: NextApiRequest, res: NextApiResponse) {
-        ...
-    },
-};
-export default requestHandler(service, options);

The getServerUser callback method is used for getting the current login user on the server side. Its implementation depends on how you authenticate users.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/server-side-rendering.html b/doc-serve/public/static/server-side-rendering.html deleted file mode 100644 index 122299332..000000000 --- a/doc-serve/public/static/server-side-rendering.html +++ /dev/null @@ -1,173 +0,0 @@ - - - Server-side rendering - - - - - - - - - - - - - - - - - - - - - - - - - -

Server-side rendering

You can use the service object to conduct CRUD operations on the server side directly without the overhead of HTTP requests. The service object contains members for each of the data model defined.

The server-side CRUD methods are similar signature with client-side hooks, except that they take an extra queryContext parameter for passing in the current login user. Like client-side hooks, the CRUD operations are fully protected by access policies defined in ZModel.

These methods are handy for implementing SSR (or custom API endpoints). Here's an example (using Next-Auth for authentication):

import service from '@zenstackhq/runtime/server';
-import { unstable_getServerSession } from 'next-auth';
-...
-
-export const getServerSideProps = async ({
-    req,
-    res,
-    params,
-}) => {
-    const session = await unstable_getServerSession(req, res, authOptions);
-    const queryContext = { user: session?.user };
-    const posts = await service.post.find(queryContext);
-    return {
-        props: { posts },
-    };
-};
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/setup-logging.html b/doc-serve/public/static/setup-logging.html deleted file mode 100644 index 2a993404f..000000000 --- a/doc-serve/public/static/setup-logging.html +++ /dev/null @@ -1,193 +0,0 @@ - - - Set up logging - - - - - - - - - - - - - - - - - - - - - - - - - -

Set up logging

ZenStack uses the following levels to control server-side logging:

  • error

    Error level logging

  • warn

    Warning level logging

  • info

    Info level logging

  • verbose

    Verbose level logging

  • query

    Detailed database query logging

By default, ZenStack prints error and warn level of logging with console.error and console.log, respectively. You can also control the logging behavior by providing a zenstack.config.json file at the root of your project.

You can turn log levels on and off in zenstack.config.json:

{
-    "log": ["verbose", "info"]
-}

The settings shown above is an shorthand for:

{
-    "log": [
-        {
-            "level": "verbose",
-            "emit": "stdout"
-        },
-        {
-            "level": "info",
-            "emit": "stdout"
-        }
-    ]
-}

You can also configure ZenStack to emit log as event instead of dumping to stdout, like:

{
-    "log": [
-        {
-            "level": "info",
-            "emit": "event"
-        }
-    ]
-}

To consume the events:

import service from '@zenstackhq/runtime';
-
-service.$on('info', (event) => {
-    console.log(event.timestamp, event.message);
-});

You can also mix and match stdout output with event emitting, like:

{
-    "log": [
-        {
-            "level": "info",
-            "emit": "stdout"
-        },
-        {
-            "level": "info",
-            "emit": "event"
-        }
-    ]
-}

The settings in zenstack.config.json controls logging of both ZenStack and the underlying Prisma instance.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/telemetry.html b/doc-serve/public/static/telemetry.html deleted file mode 100644 index b0613b01d..000000000 --- a/doc-serve/public/static/telemetry.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Telemetry - - - - - - - - - - - - - - - - - - - - - - - - - -

Telemetry

ZenStack CLI and VSCode extension sends anonymous telemetry for analyzing usage stats and finding bugs.

The information collected includes:

  • OS
  • Node.js version
  • CLI version
  • CLI command and arguments
  • CLI errors
  • Duration of command run
  • Region (based on IP)

We don't collect any telemetry at the runtime of apps using ZenStack.

We appreciate that you keep the telemetry ON so we can keep improving the toolkit. We follow the Console Do Not Track convention, and you can turn off the telemetry by setting environment variable DO_NOT_TRACK to 1:

DO_NOT_TRACK=1 npx zenstack ...
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/vscode-extension.html b/doc-serve/public/static/vscode-extension.html deleted file mode 100644 index 41c0614a9..000000000 --- a/doc-serve/public/static/vscode-extension.html +++ /dev/null @@ -1,158 +0,0 @@ - - - VSCode extension - - - - - - - - - - - - - - - - - - - - - - - - - -

VSCode extension

ZenStack VSCode extension provides syntax highlighting and error checking to improve the efficiency of your modeling work.

You can install by searching "ZenStack Language Tools" inside of VSCode, or from here directly.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-access-policy.html b/doc-serve/public/static/zmodel-access-policy.html deleted file mode 100644 index 67aef1471..000000000 --- a/doc-serve/public/static/zmodel-access-policy.html +++ /dev/null @@ -1,264 +0,0 @@ - - - Access policy - - - - - - - - - - - - - - - - - - - - - - - - - -

Access policy

Access policies use @@allow and @@deny rules to specify the eligibility of an operation over a model entity. The signatures of the attributes are:

  • @@allow

        attribute @@allow(_ operation: String, _ condition: Boolean)

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    operationComma separated list of operations to control, including "create", "read", "update", and "delete". Pass "all" as an abbriviation for including all operations.
    conditionBoolean expression indicating if the operations should be allowed
    -
  • @@deny

        attribute @@deny(_ operation: String, _ condition: Boolean)

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    operationComma separated list of operations to control, including "create", "read", "update", and "delete". Pass "all" as an abbriviation for including all operations.
    conditionBoolean expression indicating if the operations should be denied
    -

Using authentication in policy rules

It's very common to use the current login user to verdict if an operation should be permitted. Therefore, ZenStack provides a built-in auth() attribute function that evaluates to the User entity corresponding to the current user. To use the function, your ZModel file must define a User data model.

You can use auth() to:

  • Check if a user is logged in

    @@deny('all', auth() == null)
  • Access user's fields

    @@allow('update', auth().role == 'ADMIN')
  • Compare user identity

    // owner is a relation field to User model
    -@@allow('update', auth() == owner)

Accessing relation fields in policy

As you've seen in the examples above, you can access fields from relations in policy expressions. For example, to express "a user can be read by any user sharing a space" in the User model, you can directly read into its membership field.

    @@allow('read', membership?[space.members?[user == auth()]])

In most cases, when you use a "to-many" relation in a policy rule, you'll use "Collection Predicate" to express a condition. See next section for details.

Collection predicate expressions

Collection predicate expressions are boolean expressions used to express conditions over a list. It's mainly designed for building policy rules for "to-many" relations. It has three forms of syntaxes:

  • Any

    <collection>?[condition]

    Any element in collection matches condition

  • All

    <collection>![condition]

    All elements in collection match condition

  • None

    <collection>^[condition]

    None element in collection matches condition

The condition expression has direct access to fields defined in the model of collection. E.g.:

    @@allow('read', members?[user == auth()])

, in condition user == auth(), user refers to the user field in model Membership, because the collection members is resolved to Membership model.

Also, collection predicates can be nested to express complex conditions involving multi-level relation lookup. E.g.:

    @@allow('read', membership?[space.members?[user == auth()]])

In this example, user refers to user field of Membership model because space.members is resolved to Membership model.

Combining multiple rules

A data model can contain arbitrary number of policy rules. The logic of combining them is as follows:

  • The operation is rejected if any of the conditions in @@deny rules evaluate to true
  • Otherwise, the operation is permitted if any of the conditions in @@allow rules evaluate to true
  • Otherwise, the operation is rejected

Example

A simple example with Post model

model Post {
-    // reject all operations if user's not logged in
-    @@deny('all', auth() == null)
-
-    // allow all operations if the entity's owner matches the current user
-    @@allow('all', auth() == owner)
-
-    // posts are readable to anyone
-    @allow('read', true)
-}

A more complex example with multi-user spaces

model Space {
-    id String @id
-    members Membership[]
-    owner User @relation(fields: [ownerId], references: [id])
-    ownerId String
-
-    // require login
-    @@deny('all', auth() == null)
-
-    // everyone can create a space
-    @@allow('create', true)
-
-    // owner can do everything
-    @@allow('all', auth() == owner)
-
-    // any user in the space can read the space
-    //
-    // Here the <collection>?[condition] syntax is called
-    // "Collection Predicate", used to check if any element
-    // in the "collection" matches the "condition"
-    @@allow('read', members?[user == auth()])
-}
-
-// Membership is the "join-model" between User and Space
-model Membership {
-    id String @id()
-
-    // one-to-many from Space
-    space Space @relation(fields: [spaceId], references: [id])
-    spaceId String
-
-    // one-to-many from User
-    user User @relation(fields: [userId], references: [id])
-    userId String
-
-    // a user can be member of a space for only once
-    @@unique([userId, spaceId])
-
-    // require login
-    @@deny('all', auth() == null)
-
-    // space owner can create/update/delete
-    @@allow('create,update,delete', space.owner == auth())
-
-    // user can read entries for spaces which he's a member of
-    @@allow('read', space.members?[user == auth()])
-}
-
-model User {
-    id String @id
-    email String @unique
-    membership Membership[]
-    ownedSpaces Space[]
-
-    // allow signup
-    @@allow('create', true)
-
-    // user can do everything to herself; note that "this" represents
-    // the current entity
-    @@allow('all', auth() == this)
-
-    // can be read by users sharing a space
-    @@allow('read', membership?[space.members?[user == auth()]])
-}
-
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-attribute.html b/doc-serve/public/static/zmodel-attribute.html deleted file mode 100644 index 913359335..000000000 --- a/doc-serve/public/static/zmodel-attribute.html +++ /dev/null @@ -1,391 +0,0 @@ - - - Attribute - - - - - - - - - - - - - - - - - - - - - - - - - -

Attribute

Attributes decorate fields and data models and attach extra behaviors or constraints to them.

Syntax

Field attribute

Field attribute name is prefixed by a single @. Its application takes the following form:

id String @[ATTR_NAME](ARGS)?
  • [ATTR_NAME]

Attribute name. See below for a full list of attributes.

  • [ARGS]

See attribute arguments.

Data model attribute

Field attribute name is prefixed double @@. Its application takes the following form:

model Model {
-    @@[ATTR_NAME](ARGS)?
-}
  • [ATTR_NAME]

Attribute name. See below for a full list of attributes.

  • [ARGS]

See attribute arguments.

Arguments

Attribute can be declared with a list of parameters, and applied with an optional comma-separated list of arguments.

Arguments are mapped to parameters by position or by name. For example, for the @default attribute declared as:

attribute @default(_ value: ContextType)

, the following two ways of applying it are equivalent:

published Boolean @default(value: false)
published Boolean @default(false)

Parameter types

Attribute parameters are typed. The following types are supported:

  • Int

    Integer literal can be passed as argument.

    E.g., declaration:

    attribute @password(saltLength: Int?, salt: String?)
    -

    application:

    password String @password(saltLength: 10)
  • String

    String literal can be passed as argument.

    E.g., declaration:

    attribute @id(map: String?)

    application:

    id String @id(map: "_id")
  • Boolean

    Boolean literal or expression can be passed as argument.

    E.g., declaration:

    attribute @@allow(_ operation: String, _ condition: Boolean)

    application:

    @@allow("read", true)
    -@@allow("update", auth() != null)
  • ContextType

    A special type that represents the type of the field onto which the attribute is attached.

    E.g., declaration:

    attribute @default(_ value: ContextType)

    application:

    f1 String @default("hello")
    -f2 Int @default(1)
  • FieldReference

    References to fields defined in the current model.

    E.g., declaration:

    attribute @relation(
    -    _ name: String?,
    -    fields: FieldReference[]?,
    -    references: FieldReference[]?,
    -    onDelete: ReferentialAction?,
    -    onUpdate: ReferentialAction?,
    -    map: String?)

    application:

    model Model {
    -    ...
    -    // [ownerId] is a list of FieldReference
    -    owner Owner @relation(fields: [ownerId], references: [id])
    -    ownerId
    -}
  • Enum

    Attribute parameter can also be typed as predefined enum.

    E.g., declaration:

    attribute @relation(
    -    _ name: String?,
    -    fields: FieldReference[]?,
    -    references: FieldReference[]?,
    -    // ReferentialAction is a predefined enum
    -    onDelete: ReferentialAction?,
    -    onUpdate: ReferentialAction?,
    -    map: String?)

    application:

    model Model {
    -    // 'Cascade' is a predefined enum value
    -    owner Owner @relation(..., onDelete: Cascade)
    -}

An attribute parameter can be typed as any of the above type, a list of the above type, or an optional of the above type.

    model Model {
-        ...
-        f1 String
-        f2 String
-        // a list of FieldReference
-        @@unique([f1, f2])
-    }

Attribute functions

Attribute functions are used for providing values for attribute arguments, e.g., current DateTime, an autoincrement Int, etc. They can be used in place of attribute argument, like:

model Model {
-    ...
-    serial Int @default(autoincrement())
-    createdAt DateTime @default(now())
-}

You can find a list of predefined attribute functions here.

Predefined attributes

Field attributes

  • @id

    attribute @id(map: String?)

    Defines an ID on the model.

    Params:

    - - - - - - - - - - -
    NameDescription
    mapThe name of the underlying primary key constraint in the database
    -
  • @default

        attribute @default(_ value: ContextType)

    Defines a default value for a field.

    Params:

    - - - - - - - - - - -
    NameDescription
    valueThe default value expression
    -
  • @unique

        attribute @unique(map: String?)

    Defines a unique constraint for this field.

    Params:

    - - - - - - - - - - -
    NameDescription
    mapThe name of the underlying primary key constraint in the database
    -
  • @relation

        attribute @relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?)

    Defines meta information about a relation.

    Params:

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    nameThe name of the relationship
    fieldsA list of fields defined in the current model
    referencesA list of fields of the model on the other side of the relation
    onDeleteReferential action to take on delete. See details here.
    onUpdateReferential action to take on update. See details here.
    -
  • @map

        attribute @map(_ name: String)

    Maps a field name or enum value from the schema to a column with a different name in the database.

    Params:

    - - - - - - - - - - -
    NameDescription
    mapThe name of the underlying column in the database
    -
  • @updatedAt

        attribute @updatedAt()

    Automatically stores the time when a record was last updated.

  • @password

        attribute @password(saltLength: Int?, salt: String?)

    Indicates that the field is a password field and needs to be hashed before persistence.

    NOTE: ZenStack uses bcryptjs library to hash password. You can use the saltLength parameter to configure the cost of hashing, or use salt parameter to provide an explicit salt. By default, salt length of 12 is used. See bcryptjs for more details.

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    saltLengthThe length of salt to use (cost factor for the hash function)
    saltThe salt to use (a pregenerated valid salt)
    -
  • @omit

        attribute @omit()

    Indicates that the field should be omitted when read from the generated services. Commonly used together with @password attribute.

Model attributes

  • @@unique

        attribute @@unique(_ fields: FieldReference[], name: String?, map: String?)

    Defines a compound unique constraint for the specified fields.

    Params:

    - - - - - - - - - - - - - - - - - - -
    NameDescription
    fieldsA list of fields defined in the current model
    nameThe name of the unique combination of fields
    mapThe name of the underlying unique constraint in the database
    -
  • @@index

        attribute @@index(_ fields: FieldReference[], map: String?)

    Defines an index in the database.

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    fieldsA list of fields defined in the current model
    mapThe name of the underlying index in the database
    -
  • @@map

        attribute @@map(_ name: String)

    Maps the schema model name to a table with a different name, or an enum name to a different underlying enum in the database.

    Params:

    - - - - - - - - - - -
    NameDescription
    nameThe name of the underlying table or enum in the database
    -
  • @@allow

        attribute @@allow(_ operation: String, _ condition: Boolean)

    Defines an access policy that allows a set of operations when the given condition is true.

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    operationComma separated list of operations to control, including "create", "read", "update", and "delete". Pass "all" as an abbriviation for including all operations.
    conditionBoolean expression indicating if the operations should be allowed
    -
  • @@deny

        attribute @@deny(_ operation: String, _ condition: Boolean)

    Defines an access policy that denies a set of operations when the given condition is true.

    Params:

    - - - - - - - - - - - - - - -
    NameDescription
    operationComma separated list of operations to control, including "create", "read", "update", and "delete". Pass "all" as an abbriviation for including all operations.
    conditionBoolean expression indicating if the operations should be denied
    -

Predefined attribute functions

  • uuid

        function uuid(): String {}

    Generates a globally unique identifier based on the UUID spec.

  • cuid

        function cuid(): String {}

    Generates a globally unique identifier based on the CUID spec.

  • now

        function now(): DateTime {}

    Gets current date-time.

  • autoincrement

        function autoincrement(): Int {}

    Creates a sequence of integers in the underlying database and assign the incremented - values to the ID values of the created records based on the sequence.

  • dbgenerated

        function dbgenerated(expr: String): Any {}

    Represents default values that cannot be expressed in the Prisma schema (such as random()).

  • auth

        function auth(): User {}

    Gets the current login user. The return type of the function is the User data model defined in the current ZModel.

Examples

Here're some examples on using field and model attributes:

model User {
-    // unique id field with a default UUID value
-    id String @id @default(uuid())
-
-    // require email field to be unique
-    email String @unique
-
-    // password is hashed with bcrypt with length of 16, omitted when returned from the CRUD services
-    password String @password(saltLength: 16) @omit
-
-    // default to current date-time
-    createdAt DateTime @default(now())
-
-    // auto-updated when the entity is modified
-    updatedAt DateTime @updatedAt
-
-    // mapping to a different column name in database
-    description String @map("desc")
-
-    // mapping to a different table name in database
-    @@map("users")
-
-    // use @@index to specify fields to create database index for
-    @@index([email])
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-data-model.html b/doc-serve/public/static/zmodel-data-model.html deleted file mode 100644 index 91e69bd6a..000000000 --- a/doc-serve/public/static/zmodel-data-model.html +++ /dev/null @@ -1,162 +0,0 @@ - - - Data model - - - - - - - - - - - - - - - - - - - - - - - - - -

Data model

Data models represent business entities of your application.

Syntax

A data model declaration takes the following form:

model [NAME] {
-    [FIELD]*
-}
  • [NAME]:

    Name of the data model. Needs to be unique in the entire model. Needs to be a valid identifier matching regular expression [A-Za-z][a-za-z0-9_]\*.

  • [FIELD]:

    Arbitrary number of fields. See next section for details.

Note

A data model must include a String typed field named id, marked with @id attribute. The id field serves as a unique identifier for a model entity, and is mapped to the database table's primary key.

See here for more details about attributes.

Example

model User {
-    id String @id
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-data-source.html b/doc-serve/public/static/zmodel-data-source.html deleted file mode 100644 index a86eeb9e9..000000000 --- a/doc-serve/public/static/zmodel-data-source.html +++ /dev/null @@ -1,251 +0,0 @@ - - - Data source - - - - - - - - - - - - - - - - - - - - - - - - - -

Data source

Every model needs to include exactly one datasource declaration, providing information on how to connect to the underlying database.

Syntax

A data source declaration takes the following form:

datasource [NAME] {
-    provider = [PROVIDER]
-    url = [DB_URL]
-}
  • [NAME]:

    Name of the data source. Needs to be a valid identifier matching regular expression [A-Za-z][a-za-z0-9_]\*. Name is only informational and serves no other purposes.

  • [PROVIDER]:

    Name of database connector. Valid values:

    • sqlite
    • postgresql
    • mysql
    • sqlserver
    • cockroachdb
  • [DB_URL]:

    Database connection string. Either a plain string or an invocation of env function to fetch from an environment variable.

Examples

datasource db {
-    provider = "postgresql"
-    url = "postgresql://postgres:abc123@localhost:5432/todo?schema=public"
-}

It's highly recommended that you don't commit sensitive database connection string into source control. Alternatively, you can load it from an environment variable:

datasource db {
-    provider = "postgresql"
-    url = env("DATABASE_URL")
-}

Supported databases

ZenStack uses Prisma to talk to databases, so all relational databases supported by Prisma is supported by ZenStack as well.

Here's a list for your reference:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DatabaseVersion
PostgreSQL9.6
PostgreSQL10
PostgreSQL11
PostgreSQL12
PostgreSQL13
PostgreSQL14
PostgreSQL15
MySQL5.6
MySQL5.7
MySQL8
MariaDB10
SQLite*
AWS Aurora*
AWS Aurora Serverless*
Microsoft SQL Server2022
Microsoft SQL Server2019
Microsoft SQL Server2017
Azure SQL*
CockroachDB21.2.4+
-

You can find the orignal list here.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-enum.html b/doc-serve/public/static/zmodel-enum.html deleted file mode 100644 index 82814af11..000000000 --- a/doc-serve/public/static/zmodel-enum.html +++ /dev/null @@ -1,163 +0,0 @@ - - - Enum - - - - - - - - - - - - - - - - - - - - - - - - - -

Enum

Enums are container declarations for grouping constant identifiers. You can use them to express concepts like user roles, product categories, etc.

Syntax

Enum declarations take the following form:

enum [ENUM_NAME] {
-    [FIELD]*
-}
  • [ENUM_NAME]

    Name of the enum. Needs to be unique in the entire model. Needs to be a valid identifier matching regular expression [A-Za-z][a-za-z0-9_]\*.

  • [FIELD]

    Field identifier. Needs to be unique in the data model. Needs to be a valid identifier matching regular expression [A-Za-z][a-za-z0-9_]\*.

Example

enum UserRole {
-    USER
-    ADMIN
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-field-constraint.html b/doc-serve/public/static/zmodel-field-constraint.html deleted file mode 100644 index 8a9955c6a..000000000 --- a/doc-serve/public/static/zmodel-field-constraint.html +++ /dev/null @@ -1,164 +0,0 @@ - - - Field constraint - - - - - - - - - - - - - - - - - - - - - - - - - -

Field constraint

Overview

Field constraints are used for attaching constraints to field values. Unlike access policies, field constraints only apply on individual fields, and are only checked for 'create' and 'update' operations.

Internally ZenStack uses zod for validation. The checks are run in both the server-side CURD services and the clent-side React hooks. For the server side, upon validation error, HTTP 400 is returned with a body containing a message field for details. For the client side, a ValidationError is thrown.

Constraint attributes

The following attributes can be used to attach field constraints:

String

  • @length(_ min: Int?, _ max: Int?)

    Validates length of a string field.

  • @startsWith(_ text: String)

    Validates a string field value starts with the given text.

  • @endsWith(_ text: String)

    Validates a string field value ends with the given text.

  • @email()

    Validates a string field value is a valid email address.

  • @url()

    Validates a string field value is a valid url.

  • @datetime()

    Validates a string field value is a valid ISO datetime.

  • @regex(_ regex: String)

    Validates a string field value matches a regex.

Number

  • @gt(_ value: Int)

    Validates a number field is greater than the given value.

  • @gte(_ value: Int)

    Validates a number field is greater than or equal to the given value.

  • @lt(_ value: Int)

    Validates a number field is less than the given value.

  • @lte(_ value: Int)

    Validates a number field is less than or equal to the given value.

Example

model User {
-    id String @id
-    handle String @regex("^[0-9a-zA-Z]{4,16}$")
-    email String @email @endsWith("@myorg.com")
-    profileImage String? @url
-    age Int @gt(0)
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-field.html b/doc-serve/public/static/zmodel-field.html deleted file mode 100644 index 4e57e4649..000000000 --- a/doc-serve/public/static/zmodel-field.html +++ /dev/null @@ -1,182 +0,0 @@ - - - Field - - - - - - - - - - - - - - - - - - - - - - - - - -

Field

Fields are typed members of data models.

Syntax

A field declaration takes the following form:

model Model {
-    [FIELD_NAME] [FIELD_TYPE] (FIELD_ATTRIBUTES)?
-}
  • [FIELD_NAME]

    Name of the field. Needs to be unique in the containing data model. Needs to be a valid identifier matching regular expression [A-Za-z][a-za-z0-9_]\*.

  • [FIELD_TYPE]

    Type of the field. Can be a scalar type or a reference to another data model.

    The following scalar types are supported:

    • String

    • Boolean

    • Int

    • BigInt

    • Float

    • Decimal

    • Json

    • Bytes

      A field's type can be any of the scalar or reference type, a list of the aforementioned type (suffixed with []), or an optional of the aforementioned type (suffixed with ?).

  • [FIELD_ATTRIBUTES]

    Field attributes attach extra behaviors or constraints to the field. See Attribute for more information.

Example

model Post {
-    // "id" field is a mandatory unique identifier of this model
-    id String @id @default(uuid())
-
-    // fields can be DateTime
-    createdAt DateTime @default(now())
-    updatedAt DateTime @updatedAt
-
-    // or string
-    title String
-
-    // or integer
-    viewCount Int @default(0)
-
-    // and optional
-    content String?
-
-    // and a list too
-    tags String[]
-
-    // and can reference another data model too
-    comments Comment[]
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-overview.html b/doc-serve/public/static/zmodel-overview.html deleted file mode 100644 index e616910ef..000000000 --- a/doc-serve/public/static/zmodel-overview.html +++ /dev/null @@ -1,158 +0,0 @@ - - - Overview - - - - - - - - - - - - - - - - - - - - - - - - - -

Overview

ZModel, the modeling DSL of ZenStack, is the main concept that you'll deal with when using this toolkit.

The ZModel syntax is extended from the schema language of Prisma ORM. We made that choice based on several reasons:

  • CRUD heavily relies on database operations, however creating a new ORM doesn't add much value to the community, since there're already nice and mature solutions out there; so instead, we decided to extend Prisma - the overall best ORM toolkit for Typescript.

  • Prisma's schema language is simple and intuitive.

  • Extending a popular existing language lowers the learning curve, compared to inventing a new one.

Even so, this section provides detailed descriptions about all aspects of the ZModel language, so you don't need to jump over to Prisma's documentation for extra learnings.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-referential-action.html b/doc-serve/public/static/zmodel-referential-action.html deleted file mode 100644 index 56cdf1af8..000000000 --- a/doc-serve/public/static/zmodel-referential-action.html +++ /dev/null @@ -1,179 +0,0 @@ - - - Referential action - - - - - - - - - - - - - - - - - - - - - - - - - -

Referential action

Overview

When defining a relation, you can use referential action to control what happens when one side of a relation is updated or deleted, by setting the onDelete and onUpdate parameters in the @relation attribute.

    attribute @relation(
-        _ name: String?,
-        fields: FieldReference[]?,
-        references: FieldReference[]?,
-        onDelete: ReferentialAction?,
-        onUpdate: ReferentialAction?,
-        map: String?)

The ReferentialAction enum is defined as:

enum ReferentialAction {
-    Cascade
-    Restrict
-    NoAction
-    SetNull
-    SetDefault
-}
  • Cascade

    • onDelete: deleting a referenced record will trigger the deletion of referencing record.

    • onUpdate: updates the relation scalar fields if the referenced scalar fields of the dependent record are updated.

  • Restrict

    • onDelete: prevents the deletion if any referencing records exist.
    • onUpdate: prevents the identifier of a referenced record from being changed.
  • NoAction

    Similar to 'Restrict', the difference between the two is dependent on the database being used.

    See details here

  • SetNull

    • onDelete: the scalar field of the referencing object will be set to NULL.
    • onUpdate: when updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to NULL.
  • SetDefault

    • onDelete: the scalar field of the referencing object will be set to the fields default value.
    • onUpdate: the scalar field of the referencing object will be set to the fields default value.

Example

model User {
-    id String @id
-    profile Profile?
-}
-
-model Profile {
-    id String @id
-    user @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: Cascade)
-    userId String @unique
-}
- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/static/zmodel-relation.html b/doc-serve/public/static/zmodel-relation.html deleted file mode 100644 index 43054ee82..000000000 --- a/doc-serve/public/static/zmodel-relation.html +++ /dev/null @@ -1,203 +0,0 @@ - - - Relation - - - - - - - - - - - - - - - - - - - - - - - - - -

Relation

Relations are connections among data models. There're three types of relations:

  • One-to-one
  • One-to-many
  • Many-to-many

Relations are expressed with a pair of fields and together with the special @relation field attribute. One side of the relation field carries the @relation attribute to indicate how the connection is established.

One-to-one relation

The owner side of the relation declares an optional field typed as the data model of the owned side of the relation.

On the owned side, a reference field is declared with @relation attribute, together with an foreign key field storing the id of the owner entity.

model User {
-    id String @id
-    profile Profile?
-}
-
-model Profile {
-    id String @id
-    user @relation(fields: [userId], references: [id])
-    userId String @unique
-}

One-to-many relation

The owner side of the relation declares a list field typed as the data model of the owned side of the relation.

On the owned side, a reference field is declared with @relation attribute, together with an foreign key field storing the id of the owner entity.

model User {
-    id String @id
-    posts Post[]
-}
-
-model Post {
-    id String @id
-    author User? @relation(fields: [authorId], references: [id])
-    authorId String?
-}

Many-to-one relation

A join model is declared to connect the two sides of the relation, using two one-to-one relations.

Each side of the relation then establishes a one-to-many relation with the join model.

model Space {
-    id String @id
-    // one-to-many with the "join-model"
-    members Membership[]
-}
-
-// Membership is the "join-model" between User and Space
-model Membership {
-    id String @id()
-
-    // one-to-many from Space
-    space Space @relation(fields: [spaceId], references: [id])
-    spaceId String
-
-    // one-to-many from User
-    user User @relation(fields: [userId], references: [id])
-    userId String
-
-    // a user can be member of a space for only once
-    @@unique([userId, spaceId])
-}
-
-model User {
-    id String @id
-    // one-to-many with the "join-model"
-    membership Membership[]
-}
-

Referential action

When defining a relation, you can specify what happens when one side of a relation is updated or deleted. See Referential action for details.

- - - - - - - - - - - - - -
\ No newline at end of file diff --git a/doc-serve/public/telemetry.md b/doc-serve/public/telemetry.md deleted file mode 100644 index 42540dac0..000000000 --- a/doc-serve/public/telemetry.md +++ /dev/null @@ -1,21 +0,0 @@ -# Telemetry - -ZenStack CLI and VSCode extension sends anonymous telemetry for analyzing usage stats and finding bugs. - -The information collected includes: - -- OS -- Node.js version -- CLI version -- CLI command and arguments -- CLI errors -- Duration of command run -- Region (based on IP) - -We don't collect any telemetry at the runtime of apps using ZenStack. - -We appreciate that you keep the telemetry ON so we can keep improving the toolkit. We follow the [Console Do Not Track](https://consoledonottrack.com/ ':target=blank') convention, and you can turn off the telemetry by setting environment variable `DO_NOT_TRACK` to `1`: - -```bash -DO_NOT_TRACK=1 npx zenstack ... -``` diff --git a/doc-serve/public/vscode-extension.md b/doc-serve/public/vscode-extension.md deleted file mode 100644 index c48150116..000000000 --- a/doc-serve/public/vscode-extension.md +++ /dev/null @@ -1,5 +0,0 @@ -# VSCode extension - -ZenStack VSCode extension provides syntax highlighting and error checking to improve the efficiency of your modeling work. - -You can install by searching "ZenStack Language Tools" inside of VSCode, or from [here](https://marketplace.visualstudio.com/items?itemName=zenstack.zenstack) directly. diff --git a/doc-serve/public/zmodel-access-policy.md b/doc-serve/public/zmodel-access-policy.md deleted file mode 100644 index 9ecdcd83f..000000000 --- a/doc-serve/public/zmodel-access-policy.md +++ /dev/null @@ -1,203 +0,0 @@ -# Access policy - -Access policies use `@@allow` and `@@deny` rules to specify the eligibility of an operation over a model entity. The signatures of the attributes are: - -- `@@allow` - - ```zmodel - attribute @@allow(_ operation: String, _ condition: Boolean) - ``` - - _Params_: - - | Name | Description | - | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | operation | Comma separated list of operations to control, including `"create"`, `"read"`, `"update"`, and `"delete"`. Pass` "all"` as an abbriviation for including all operations. | - | condition | Boolean expression indicating if the operations should be allowed | - -- `@@deny` - - ```zmodel - attribute @@deny(_ operation: String, _ condition: Boolean) - ``` - - _Params_: - - | Name | Description | - | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | operation | Comma separated list of operations to control, including `"create"`, `"read"`, `"update"`, and `"delete"`. Pass` "all"` as an abbriviation for including all operations. | - | condition | Boolean expression indicating if the operations should be denied | - -## Using authentication in policy rules - -It's very common to use the current login user to verdict if an operation should be permitted. Therefore, ZenStack provides a built-in `auth()` attribute function that evaluates to the `User` entity corresponding to the current user. To use the function, your ZModel file must define a `User` data model. - -You can use `auth()` to: - -- Check if a user is logged in - - ```zmodel - @@deny('all', auth() == null) - ``` - -- Access user's fields - - ```zmodel - @@allow('update', auth().role == 'ADMIN') - ``` - -- Compare user identity - - ```zmodel - // owner is a relation field to User model - @@allow('update', auth() == owner) - ``` - -## Accessing relation fields in policy - -As you've seen in the examples above, you can access fields from relations in policy expressions. For example, to express "a user can be read by any user sharing a space" in the `User` model, you can directly read into its `membership` field. - -```zmodel - @@allow('read', membership?[space.members?[user == auth()]]) -``` - -In most cases, when you use a "to-many" relation in a policy rule, you'll use "Collection Predicate" to express a condition. See [next section](#collection-predicate-expressions) for details. - -## Collection predicate expressions - -Collection predicate expressions are boolean expressions used to express conditions over a list. It's mainly designed for building policy rules for "to-many" relations. It has three forms of syntaxes: - -- Any - - ``` - ?[condition] - ``` - - Any element in `collection` matches `condition` - -- All - - ``` - ![condition] - ``` - - All elements in `collection` match `condition` - -- None - - ``` - ^[condition] - ``` - - None element in `collection` matches `condition` - -The `condition` expression has direct access to fields defined in the model of `collection`. E.g.: - -```zmodel - @@allow('read', members?[user == auth()]) -``` - -, in condition `user == auth()`, `user` refers to the `user` field in model `Membership`, because the collection `members` is resolved to `Membership` model. - -Also, collection predicates can be nested to express complex conditions involving multi-level relation lookup. E.g.: - -```zmodel - @@allow('read', membership?[space.members?[user == auth()]]) -``` - -In this example, `user` refers to `user` field of `Membership` model because `space.members` is resolved to `Membership` model. - -## Combining multiple rules - -A data model can contain arbitrary number of policy rules. The logic of combining them is as follows: - -- The operation is rejected if any of the conditions in `@@deny` rules evaluate to `true` -- Otherwise, the operation is permitted if any of the conditions in `@@allow` rules evaluate to `true` -- Otherwise, the operation is rejected - -## Example - -### A simple example with Post model - -```zmodel -model Post { - // reject all operations if user's not logged in - @@deny('all', auth() == null) - - // allow all operations if the entity's owner matches the current user - @@allow('all', auth() == owner) - - // posts are readable to anyone - @allow('read', true) -} -``` - -### A more complex example with multi-user spaces - -```zmodel -model Space { - id String @id - members Membership[] - owner User @relation(fields: [ownerId], references: [id]) - ownerId String - - // require login - @@deny('all', auth() == null) - - // everyone can create a space - @@allow('create', true) - - // owner can do everything - @@allow('all', auth() == owner) - - // any user in the space can read the space - // - // Here the ?[condition] syntax is called - // "Collection Predicate", used to check if any element - // in the "collection" matches the "condition" - @@allow('read', members?[user == auth()]) -} - -// Membership is the "join-model" between User and Space -model Membership { - id String @id() - - // one-to-many from Space - space Space @relation(fields: [spaceId], references: [id]) - spaceId String - - // one-to-many from User - user User @relation(fields: [userId], references: [id]) - userId String - - // a user can be member of a space for only once - @@unique([userId, spaceId]) - - // require login - @@deny('all', auth() == null) - - // space owner can create/update/delete - @@allow('create,update,delete', space.owner == auth()) - - // user can read entries for spaces which he's a member of - @@allow('read', space.members?[user == auth()]) -} - -model User { - id String @id - email String @unique - membership Membership[] - ownedSpaces Space[] - - // allow signup - @@allow('create', true) - - // user can do everything to herself; note that "this" represents - // the current entity - @@allow('all', auth() == this) - - // can be read by users sharing a space - @@allow('read', membership?[space.members?[user == auth()]]) -} - -``` diff --git a/doc-serve/public/zmodel-attribute.md b/doc-serve/public/zmodel-attribute.md deleted file mode 100644 index 3a9c26be8..000000000 --- a/doc-serve/public/zmodel-attribute.md +++ /dev/null @@ -1,480 +0,0 @@ -# Attribute - -Attributes decorate fields and data models and attach extra behaviors or constraints to them. - -## Syntax - -### Field attribute - -Field attribute name is prefixed by a single `@`. Its application takes the following form: - -```zmodel -id String @[ATTR_NAME](ARGS)? -``` - -- **[ATTR_NAME]** - -Attribute name. See [below](#built-in-attributes) for a full list of attributes. - -- **[ARGS]** - -See [attribute arguments](#attribute-arguments). - -### Data model attribute - -Field attribute name is prefixed double `@@`. Its application takes the following form: - -```zmodel -model Model { - @@[ATTR_NAME](ARGS)? -} -``` - -- **[ATTR_NAME]** - -Attribute name. See [below](#built-in-attributes) for a full list of attributes. - -- **[ARGS]** - -See [attribute arguments](#attribute-arguments). - -### Arguments - -Attribute can be declared with a list of parameters, and applied with an optional comma-separated list of arguments. - -Arguments are mapped to parameters by position or by name. For example, for the `@default` attribute declared as: - -```zmodel -attribute @default(_ value: ContextType) -``` - -, the following two ways of applying it are equivalent: - -```zmodel -published Boolean @default(value: false) -``` - -```zmodel -published Boolean @default(false) -``` - -## Parameter types - -Attribute parameters are typed. The following types are supported: - -- Int - - Integer literal can be passed as argument. - - E.g., declaration: - - ```zmodel - attribute @password(saltLength: Int?, salt: String?) - - ``` - - application: - - ```zmodel - password String @password(saltLength: 10) - ``` - -- String - - String literal can be passed as argument. - - E.g., declaration: - - ```zmodel - attribute @id(map: String?) - ``` - - application: - - ```zmodel - id String @id(map: "_id") - ``` - -- Boolean - - Boolean literal or expression can be passed as argument. - - E.g., declaration: - - ```zmodel - attribute @@allow(_ operation: String, _ condition: Boolean) - ``` - - application: - - ```zmodel - @@allow("read", true) - @@allow("update", auth() != null) - ``` - -- ContextType - - A special type that represents the type of the field onto which the attribute is attached. - - E.g., declaration: - - ```zmodel - attribute @default(_ value: ContextType) - ``` - - application: - - ```zmodel - f1 String @default("hello") - f2 Int @default(1) - ``` - -- FieldReference - - References to fields defined in the current model. - - E.g., declaration: - - ```zmodel - attribute @relation( - _ name: String?, - fields: FieldReference[]?, - references: FieldReference[]?, - onDelete: ReferentialAction?, - onUpdate: ReferentialAction?, - map: String?) - ``` - - application: - - ```zmodel - model Model { - ... - // [ownerId] is a list of FieldReference - owner Owner @relation(fields: [ownerId], references: [id]) - ownerId - } - ``` - -- Enum - - Attribute parameter can also be typed as predefined enum. - - E.g., declaration: - - ```zmodel - attribute @relation( - _ name: String?, - fields: FieldReference[]?, - references: FieldReference[]?, - // ReferentialAction is a predefined enum - onDelete: ReferentialAction?, - onUpdate: ReferentialAction?, - map: String?) - ``` - - application: - - ```zmodel - model Model { - // 'Cascade' is a predefined enum value - owner Owner @relation(..., onDelete: Cascade) - } - ``` - -An attribute parameter can be typed as any of the above type, a list of the above type, or an optional of the above type. - -```zmodel - model Model { - ... - f1 String - f2 String - // a list of FieldReference - @@unique([f1, f2]) - } -``` - -## Attribute functions - -Attribute functions are used for providing values for attribute arguments, e.g., current `DateTime`, an autoincrement `Int`, etc. They can be used in place of attribute argument, like: - -```zmodel -model Model { - ... - serial Int @default(autoincrement()) - createdAt DateTime @default(now()) -} -``` - -You can find a list of predefined attribute functions [here](#predefined-attribute-functions). - -## Predefined attributes - -### Field attributes - -- `@id` - - ```zmodel - attribute @id(map: String?) - ``` - - Defines an ID on the model. - - _Params_: - - | Name | Description | - | ---- | ----------------------------------------------------------------- | - | map | The name of the underlying primary key constraint in the database | - -- `@default` - - ```zmodel - attribute @default(_ value: ContextType) - ``` - - Defines a default value for a field. - - _Params_: - - | Name | Description | - | ----- | ---------------------------- | - | value | The default value expression | - -- `@unique` - - ```zmodel - attribute @unique(map: String?) - ``` - - Defines a unique constraint for this field. - - _Params_: - - | Name | Description | - | ---- | ----------------------------------------------------------------- | - | map | The name of the underlying primary key constraint in the database | - -- `@relation` - - ```zmodel - attribute @relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?) - ``` - - Defines meta information about a relation. - - _Params_: - - | Name | Description | - | ---------- | --------------------------------------------------------------------------------------- | - | name | The name of the relationship | - | fields | A list of fields defined in the current model | - | references | A list of fields of the model on the other side of the relation | - | onDelete | Referential action to take on delete. See details [here](zmodel-referential-action.md). | - | onUpdate | Referential action to take on update. See details [here](zmodel-referential-action.md). | - -- `@map` - - ```zmodel - attribute @map(_ name: String) - ``` - - Maps a field name or enum value from the schema to a column with a different name in the database. - - _Params_: - - | Name | Description | - | ---- | ------------------------------------------------- | - | map | The name of the underlying column in the database | - -- `@updatedAt` - - ```zmodel - attribute @updatedAt() - ``` - - Automatically stores the time when a record was last updated. - -- `@password` - - ```zmodel - attribute @password(saltLength: Int?, salt: String?) - ``` - - Indicates that the field is a password field and needs to be hashed before persistence. - - _NOTE_: ZenStack uses `bcryptjs` library to hash password. You can use the `saltLength` parameter to configure the cost of hashing, or use `salt` parameter to provide an explicit salt. By default, salt length of 12 is used. See [bcryptjs](https://www.npmjs.com/package/bcryptjs ':target=blank') for more details. - - _Params_: - - | Name | Description | - | ---------- | ------------------------------------------------------------- | - | saltLength | The length of salt to use (cost factor for the hash function) | - | salt | The salt to use (a pregenerated valid salt) | - -- `@omit` - - ```zmodel - attribute @omit() - ``` - - Indicates that the field should be omitted when read from the generated services. Commonly used together with `@password` attribute. - -### Model attributes - -- `@@unique` - - ```zmodel - attribute @@unique(_ fields: FieldReference[], name: String?, map: String?) - ``` - - Defines a compound unique constraint for the specified fields. - - _Params_: - - | Name | Description | - | ------ | ------------------------------------------------------------ | - | fields | A list of fields defined in the current model | - | name | The name of the unique combination of fields | - | map | The name of the underlying unique constraint in the database | - -- `@@index` - - ```zmodel - attribute @@index(_ fields: FieldReference[], map: String?) - ``` - - Defines an index in the database. - - _Params_: - - | Name | Description | - | ------ | ------------------------------------------------ | - | fields | A list of fields defined in the current model | - | map | The name of the underlying index in the database | - -- `@@map` - - ```zmodel - attribute @@map(_ name: String) - ``` - - Maps the schema model name to a table with a different name, or an enum name to a different underlying enum in the database. - - _Params_: - - | Name | Description | - | ---- | -------------------------------------------------------- | - | name | The name of the underlying table or enum in the database | - -- `@@allow` - - ```zmodel - attribute @@allow(_ operation: String, _ condition: Boolean) - ``` - - Defines an access policy that allows a set of operations when the given condition is true. - - _Params_: - - | Name | Description | - | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | operation | Comma separated list of operations to control, including `"create"`, `"read"`, `"update"`, and `"delete"`. Pass` "all"` as an abbriviation for including all operations. | - | condition | Boolean expression indicating if the operations should be allowed | - -- `@@deny` - - ```zmodel - attribute @@deny(_ operation: String, _ condition: Boolean) - ``` - - Defines an access policy that denies a set of operations when the given condition is true. - - _Params_: - - | Name | Description | - | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | operation | Comma separated list of operations to control, including `"create"`, `"read"`, `"update"`, and `"delete"`. Pass` "all"` as an abbriviation for including all operations. | - | condition | Boolean expression indicating if the operations should be denied | - -## Predefined attribute functions - -- `uuid` - - ```zmodel - function uuid(): String {} - ``` - - Generates a globally unique identifier based on the UUID spec. - -- `cuid` - - ```zmodel - function cuid(): String {} - ``` - - Generates a globally unique identifier based on the [CUID](https://github.com/ericelliott/cuid) spec. - -- `now` - - ```zmodel - function now(): DateTime {} - ``` - - Gets current date-time. - -- `autoincrement` - - ```zmodel - function autoincrement(): Int {} - ``` - - Creates a sequence of integers in the underlying database and assign the incremented - values to the ID values of the created records based on the sequence. - -- `dbgenerated` - - ```zmodel - function dbgenerated(expr: String): Any {} - ``` - - Represents default values that cannot be expressed in the Prisma schema (such as random()). - -- `auth` - - ```zmodel - function auth(): User {} - ``` - - Gets the current login user. The return type of the function is the `User` data model defined in the current ZModel. - -## Examples - -Here're some examples on using field and model attributes: - -```zmodel -model User { - // unique id field with a default UUID value - id String @id @default(uuid()) - - // require email field to be unique - email String @unique - - // password is hashed with bcrypt with length of 16, omitted when returned from the CRUD services - password String @password(saltLength: 16) @omit - - // default to current date-time - createdAt DateTime @default(now()) - - // auto-updated when the entity is modified - updatedAt DateTime @updatedAt - - // mapping to a different column name in database - description String @map("desc") - - // mapping to a different table name in database - @@map("users") - - // use @@index to specify fields to create database index for - @@index([email]) -} -``` diff --git a/doc-serve/public/zmodel-data-model.md b/doc-serve/public/zmodel-data-model.md deleted file mode 100644 index b93d3aa62..000000000 --- a/doc-serve/public/zmodel-data-model.md +++ /dev/null @@ -1,35 +0,0 @@ -# Data model - -Data models represent business entities of your application. - -## Syntax - -A data model declaration takes the following form: - -```zmodel -model [NAME] { - [FIELD]* -} -``` - -- **[NAME]**: - - Name of the data model. Needs to be unique in the entire model. Needs to be a valid identifier matching regular expression `[A-Za-z][a-za-z0-9_]\*`. - -- **[FIELD]**: - - Arbitrary number of fields. See [next section](zmodel-field.md) for details. - -## Note - -A data model must include a `String` typed field named `id`, marked with `@id` attribute. The `id` field serves as a unique identifier for a model entity, and is mapped to the database table's primary key. - -See [here](zmodel-attribute.md) for more details about attributes. - -## Example - -```zmodel -model User { - id String @id -} -``` diff --git a/doc-serve/public/zmodel-data-source.md b/doc-serve/public/zmodel-data-source.md deleted file mode 100644 index 1b93c84d9..000000000 --- a/doc-serve/public/zmodel-data-source.md +++ /dev/null @@ -1,80 +0,0 @@ -# Data source - -Every model needs to include exactly one `datasource` declaration, providing information on how to connect to the underlying database. - -## Syntax - -A data source declaration takes the following form: - -```zmodel -datasource [NAME] { - provider = [PROVIDER] - url = [DB_URL] -} -``` - -- **[NAME]**: - - Name of the data source. Needs to be a valid identifier matching regular expression `[A-Za-z][a-za-z0-9_]\*`. Name is only informational and serves no other purposes. - -- **[PROVIDER]**: - - Name of database connector. Valid values: - - - sqlite - - postgresql - - mysql - - sqlserver - - cockroachdb - -- **[DB_URL]**: - - Database connection string. Either a plain string or an invocation of `env` function to fetch from an environment variable. - -## Examples - -```zmodel -datasource db { - provider = "postgresql" - url = "postgresql://postgres:abc123@localhost:5432/todo?schema=public" -} -``` - -It's highly recommended that you don't commit sensitive database connection string into source control. Alternatively, you can load it from an environment variable: - -```zmodel -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} -``` - -## Supported databases - -ZenStack uses [Prisma](https://prisma.io ':target=_blank') to talk to databases, so all relational databases supported by Prisma is supported by ZenStack as well. - -Here's a list for your reference: - -| Database | Version | -| --------------------- | ------- | -| PostgreSQL | 9.6 | -| PostgreSQL | 10 | -| PostgreSQL | 11 | -| PostgreSQL | 12 | -| PostgreSQL | 13 | -| PostgreSQL | 14 | -| PostgreSQL | 15 | -| MySQL | 5.6 | -| MySQL | 5.7 | -| MySQL | 8 | -| MariaDB | 10 | -| SQLite | \* | -| AWS Aurora | \* | -| AWS Aurora Serverless | \* | -| Microsoft SQL Server | 2022 | -| Microsoft SQL Server | 2019 | -| Microsoft SQL Server | 2017 | -| Azure SQL | \* | -| CockroachDB | 21.2.4+ | - -You can find the orignal list [here](https://www.prisma.io/docs/reference/database-reference/supported-databases ':target=_blank'). diff --git a/doc-serve/public/zmodel-enum.md b/doc-serve/public/zmodel-enum.md deleted file mode 100644 index 56a2428c9..000000000 --- a/doc-serve/public/zmodel-enum.md +++ /dev/null @@ -1,30 +0,0 @@ -# Enum - -Enums are container declarations for grouping constant identifiers. You can use them to express concepts like user roles, product categories, etc. - -## Syntax - -Enum declarations take the following form: - -```prsima -enum [ENUM_NAME] { - [FIELD]* -} -``` - -- **[ENUM_NAME]** - - Name of the enum. Needs to be unique in the entire model. Needs to be a valid identifier matching regular expression `[A-Za-z][a-za-z0-9_]\*`. - -- **[FIELD]** - - Field identifier. Needs to be unique in the data model. Needs to be a valid identifier matching regular expression `[A-Za-z][a-za-z0-9_]\*`. - -## Example - -```zmodel -enum UserRole { - USER - ADMIN -} -``` diff --git a/doc-serve/public/zmodel-field-constraint.md b/doc-serve/public/zmodel-field-constraint.md deleted file mode 100644 index bcc416a73..000000000 --- a/doc-serve/public/zmodel-field-constraint.md +++ /dev/null @@ -1,71 +0,0 @@ -# Field constraint - -## Overview - -Field constraints are used for attaching constraints to field values. Unlike access policies, field constraints only apply on individual fields, and are only checked for 'create' and 'update' operations. - -Internally ZenStack uses [zod](https://github.com/colinhacks/zod ':target=blank') for validation. The checks are run in both the server-side CURD services and the clent-side React hooks. For the server side, upon validation error, HTTP 400 is returned with a body containing a `message` field for details. For the client side, a `ValidationError` is thrown. - -## Constraint attributes - -The following attributes can be used to attach field constraints: - -### String - -- `@length(_ min: Int?, _ max: Int?)` - - Validates length of a string field. - -- `@startsWith(_ text: String)` - - Validates a string field value starts with the given text. - -- `@endsWith(_ text: String)` - - Validates a string field value ends with the given text. - -- `@email()` - - Validates a string field value is a valid email address. - -- `@url()` - - Validates a string field value is a valid url. - -- `@datetime()` - - Validates a string field value is a valid ISO datetime. - -- `@regex(_ regex: String)` - - Validates a string field value matches a regex. - -### Number - -- `@gt(_ value: Int)` - - Validates a number field is greater than the given value. - -- `@gte(_ value: Int)` - - Validates a number field is greater than or equal to the given value. - -- `@lt(_ value: Int)` - - Validates a number field is less than the given value. - -- `@lte(_ value: Int)` - - Validates a number field is less than or equal to the given value. - -## Example - -```zmodel -model User { - id String @id - handle String @regex("^[0-9a-zA-Z]{4,16}$") - email String @email @endsWith("@myorg.com") - profileImage String? @url - age Int @gt(0) -} -``` diff --git a/doc-serve/public/zmodel-field.md b/doc-serve/public/zmodel-field.md deleted file mode 100644 index a989228b5..000000000 --- a/doc-serve/public/zmodel-field.md +++ /dev/null @@ -1,66 +0,0 @@ -# Field - -Fields are typed members of data models. - -## Syntax - -A field declaration takes the following form: - -```zmodel -model Model { - [FIELD_NAME] [FIELD_TYPE] (FIELD_ATTRIBUTES)? -} -``` - -- **[FIELD_NAME]** - - Name of the field. Needs to be unique in the containing data model. Needs to be a valid identifier matching regular expression `[A-Za-z][a-za-z0-9_]\*`. - -- **[FIELD_TYPE]** - - Type of the field. Can be a scalar type or a reference to another data model. - - The following scalar types are supported: - - - String - - Boolean - - Int - - BigInt - - Float - - Decimal - - Json - - Bytes - - A field's type can be any of the scalar or reference type, a list of the aforementioned type (suffixed with `[]`), or an optional of the aforementioned type (suffixed with `?`). - -- **[FIELD_ATTRIBUTES]** - - Field attributes attach extra behaviors or constraints to the field. See [Attribute](zmodel-attribute.md) for more information. - -## Example - -```zmodel -model Post { - // "id" field is a mandatory unique identifier of this model - id String @id @default(uuid()) - - // fields can be DateTime - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - // or string - title String - - // or integer - viewCount Int @default(0) - - // and optional - content String? - - // and a list too - tags String[] - - // and can reference another data model too - comments Comment[] -} -``` diff --git a/doc-serve/public/zmodel-overview.md b/doc-serve/public/zmodel-overview.md deleted file mode 100644 index a3fcfc02f..000000000 --- a/doc-serve/public/zmodel-overview.md +++ /dev/null @@ -1,13 +0,0 @@ -# Overview - -**ZModel**, the modeling DSL of ZenStack, is the main concept that you'll deal with when using this toolkit. - -The **ZModel** syntax is extended from the schema language of [Prisma ORM](https://prisma.io). We made that choice based on several reasons: - -- CRUD heavily relies on database operations, however creating a new ORM doesn't add much value to the community, since there're already nice and mature solutions out there; so instead, we decided to extend Prisma - the overall best ORM toolkit for Typescript. - -- Prisma's schema language is simple and intuitive. - -- Extending a popular existing language lowers the learning curve, compared to inventing a new one. - -Even so, this section provides detailed descriptions about all aspects of the ZModel language, so you don't need to jump over to Prisma's documentation for extra learnings. diff --git a/doc-serve/public/zmodel-referential-action.md b/doc-serve/public/zmodel-referential-action.md deleted file mode 100644 index 30cec3ada..000000000 --- a/doc-serve/public/zmodel-referential-action.md +++ /dev/null @@ -1,68 +0,0 @@ -# Referential action - -## Overview - -When defining a relation, you can use referential action to control what happens when one side of a relation is updated or deleted, by setting the `onDelete` and `onUpdate` parameters in the `@relation` attribute. - -```zmodel - attribute @relation( - _ name: String?, - fields: FieldReference[]?, - references: FieldReference[]?, - onDelete: ReferentialAction?, - onUpdate: ReferentialAction?, - map: String?) -``` - -The `ReferentialAction` enum is defined as: - -```zmodel -enum ReferentialAction { - Cascade - Restrict - NoAction - SetNull - SetDefault -} -``` - -- `Cascade` - - - **onDelete**: deleting a referenced record will trigger the deletion of referencing record. - - - **onUpdate**: updates the relation scalar fields if the referenced scalar fields of the dependent record are updated. - -- `Restrict` - - - **onDelete**: prevents the deletion if any referencing records exist. - - **onUpdate**: prevents the identifier of a referenced record from being changed. - -- `NoAction` - - Similar to 'Restrict', the difference between the two is dependent on the database being used. - - See details [here](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#noaction ':target=blank') - -- `SetNull` - - - **onDelete**: the scalar field of the referencing object will be set to NULL. - - **onUpdate**: when updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to NULL. - -- `SetDefault` - - **onDelete**: the scalar field of the referencing object will be set to the fields default value. - - **onUpdate**: the scalar field of the referencing object will be set to the fields default value. - -## Example - -```zmodel -model User { - id String @id - profile Profile? -} - -model Profile { - id String @id - user @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: Cascade) - userId String @unique -} -``` diff --git a/doc-serve/public/zmodel-relation.md b/doc-serve/public/zmodel-relation.md deleted file mode 100644 index 02ca4e274..000000000 --- a/doc-serve/public/zmodel-relation.md +++ /dev/null @@ -1,88 +0,0 @@ -# Relation - -Relations are connections among data models. There're three types of relations: - -- One-to-one -- One-to-many -- Many-to-many - -Relations are expressed with a pair of fields and together with the special `@relation` field attribute. One side of the relation field carries the `@relation` attribute to indicate how the connection is established. - -## One-to-one relation - -The _owner_ side of the relation declares an optional field typed as the data model of the _owned_ side of the relation. - -On the _owned_ side, a reference field is declared with `@relation` attribute, together with an **foreign key** field storing the id of the owner entity. - -```zmodel -model User { - id String @id - profile Profile? -} - -model Profile { - id String @id - user @relation(fields: [userId], references: [id]) - userId String @unique -} -``` - -## One-to-many relation - -The _owner_ side of the relation declares a list field typed as the data model of the _owned_ side of the relation. - -On the _owned_ side, a reference field is declared with `@relation` attribute, together with an **foreign key** field storing the id of the owner entity. - -```zmodel -model User { - id String @id - posts Post[] -} - -model Post { - id String @id - author User? @relation(fields: [authorId], references: [id]) - authorId String? -} -``` - -## Many-to-one relation - -A _join model_ is declared to connect the two sides of the relation, using two one-to-one relations. - -Each side of the relation then establishes a one-to-many relation with the _join model_. - -```zmodel -model Space { - id String @id - // one-to-many with the "join-model" - members Membership[] -} - -// Membership is the "join-model" between User and Space -model Membership { - id String @id() - - // one-to-many from Space - space Space @relation(fields: [spaceId], references: [id]) - spaceId String - - // one-to-many from User - user User @relation(fields: [userId], references: [id]) - userId String - - // a user can be member of a space for only once - @@unique([userId, spaceId]) -} - -model User { - id String @id - // one-to-many with the "join-model" - membership Membership[] -} - -``` - -## Referential action - -When defining a relation, you can specify what happens when one side of a relation is updated or deleted. See [Referential action](zmodel-referential-action.md) for details. diff --git a/doc-serve/vercel.json b/doc-serve/vercel.json deleted file mode 100644 index c384e2204..000000000 --- a/doc-serve/vercel.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "trailingSlash": false -} diff --git a/docs/building-your-app.md b/docs/building-your-app.md index e25d70ac6..f169d1639 100644 --- a/docs/building-your-app.md +++ b/docs/building-your-app.md @@ -11,10 +11,7 @@ First you should mount the generated server-side code as a Next.js API endpoint. import { authOptions } from '@api/auth/[...nextauth]'; import service from '@zenstackhq/runtime'; -import { - requestHandler, - type RequestHandlerOptions, -} from '@zenstackhq/runtime/server'; +import { requestHandler, type RequestHandlerOptions } from '@zenstackhq/runtime/server'; import { NextApiRequest, NextApiResponse } from 'next'; import { unstable_getServerSession } from 'next-auth'; diff --git a/docs/index.html b/docs/index.html index 65b47cac0..b06ee2cd4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,9 +2,7 @@ - - ZenStack - toolkit for building CRUD apps with Next.js + Typescript - + ZenStack - toolkit for building CRUD apps with Next.js + Typescript - + - + - + - + - + - + - +
Please wait...
@@ -75,8 +52,7 @@ subMaxLevel: 2, ga: 'UA-250121623-1', alias: { - '.*?/changelog': - 'https://raw.githubusercontent.com/zenstackhq/zenstack/main/CHANGELOG.md', + '.*?/changelog': 'https://raw.githubusercontent.com/zenstackhq/zenstack/main/CHANGELOG.md', }, routerMode: 'history', }; diff --git a/docs/runtime-api.md b/docs/runtime-api.md index 07d7b4833..8d017da2a 100644 --- a/docs/runtime-api.md +++ b/docs/runtime-api.md @@ -87,20 +87,13 @@ export type HooksError = { ### `get` ```ts -function get( - id: string | undefined, - args?: UserFindFirstArgs, - options?: RequestOptions -): SWRResponse; +function get(id: string | undefined, args?: UserFindFirstArgs, options?: RequestOptions): SWRResponse; ``` ### `find` ```ts -function find( - args?: UserFindManyArgs, - options?: RequestOptions -): SWRResponse; +function find(args?: UserFindManyArgs, options?: RequestOptions): SWRResponse; ``` ### `create` diff --git a/docs/script/sitemap.js b/docs/script/sitemap.js index cd9ee52cd..916e552ea 100644 --- a/docs/script/sitemap.js +++ b/docs/script/sitemap.js @@ -6,20 +6,16 @@ import fs from 'fs'; const links = [ { url: '/', changefreq: 'daily' }, { url: '/changelog', changefreq: 'daily' }, - ...globbySync(['./**/[!_]?*.md', '!node_modules', '!README.md']).map( - (path) => ({ - url: `/${path.replace('.md', '')}`, - changefreq: 'daily', - }) - ), + ...globbySync(['./**/[!_]?*.md', '!node_modules', '!README.md']).map((path) => ({ + url: `/${path.replace('.md', '')}`, + changefreq: 'daily', + })), ]; console.log('Sitemap entries:'); console.log(links); const stream = new SitemapStream({ hostname: 'https://zenstack.dev' }); -const content = ( - await streamToPromise(Readable.from(links).pipe(stream)) -).toString('utf-8'); +const content = (await streamToPromise(Readable.from(links).pipe(stream))).toString('utf-8'); fs.writeFileSync('../doc-serve/public/sitemap.xml', content); diff --git a/docs/script/ssg.js b/docs/script/ssg.js index 70b1ea7aa..d808e48d3 100644 --- a/docs/script/ssg.js +++ b/docs/script/ssg.js @@ -8,12 +8,10 @@ const siteUrl = 'http://localhost:8765'; const urls = [ { route: 'index', url: `${siteUrl}/` }, { route: 'changelog', url: `${siteUrl}/changelog` }, - ...globbySync(['./**/[!_]?*.md', '!node_modules', '!README.md']).map( - (path) => ({ - route: path.replace('.md', ''), - url: `${siteUrl}/${path.replace('.md', '')}`, - }) - ), + ...globbySync(['./**/[!_]?*.md', '!node_modules', '!README.md']).map((path) => ({ + route: path.replace('.md', ''), + url: `${siteUrl}/${path.replace('.md', '')}`, + })), ]; console.log('Generating static content for', urls.length, 'urls'); @@ -26,9 +24,7 @@ for (let i = 0; i < urls.length; i++) { await page.goto(url); await page.waitForNetworkIdle({ idleTime: 2000, timeout: 5000 }); - await page.evaluate( - "document.querySelectorAll('script').forEach(e => e.remove())" - ); + await page.evaluate("document.querySelectorAll('script').forEach(e => e.remove())"); const content = await page.content(); fs.writeFileSync(`../doc-serve/public/static/${route}.html`, content); spinner.succeed(); diff --git a/docs/sitemap.js b/docs/sitemap.js new file mode 100644 index 000000000..109e8e526 --- /dev/null +++ b/docs/sitemap.js @@ -0,0 +1,20 @@ +import { globbySync } from 'globby'; +import { SitemapStream, streamToPromise } from 'sitemap'; +import { Readable } from 'stream'; +import fs from 'fs'; + +const links = [ + { url: '/', changefreq: 'daily' }, + ...globbySync(['./**/[!_]?*.md', '!node_modules', '!README.md']).map((path) => ({ + url: `/${path.replace('.md', '')}`, + changefreq: 'daily', + })), +]; + +console.log('Sitemap entries:'); +console.log(links); + +const stream = new SitemapStream({ hostname: 'https://zenstack.dev' }); +const content = (await streamToPromise(Readable.from(links).pipe(stream))).toString('utf-8'); + +fs.writeFileSync('./sitemap.xml', content); diff --git a/docs/vercel.json b/docs/vercel.json new file mode 100644 index 000000000..5f1824307 --- /dev/null +++ b/docs/vercel.json @@ -0,0 +1,13 @@ +{ + "rewrites": [ + { "source": "/sitemap.xml", "destination": "/sitemap.xml" }, + { + "source": "/8cd31ff7afe74424abbec00b65f04ae8.txt", + "destination": "/8cd31ff7afe74424abbec00b65f04ae8.txt" + }, + { "source": "/:path*", "destination": "/index.html" } + ], + "github": { + "silent": true + } +} diff --git a/package.json b/package.json index a247b9790..3ab50a562 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { "name": "zenstack-monorepo", - "version": "0.5.0", + "version": "1.0.0-alpha.27", "description": "", "scripts": { "build": "pnpm -r build", "test": "pnpm -r run test --silent", "lint": "pnpm -r lint", "publish-all": "pnpm --filter \"./packages/**\" -r publish", - "publish-dev": "pnpm --filter \"./packages/**\" -r publish --tag dev" + "publish-dev": "pnpm --filter \"./packages/**\" -r publish --tag dev", + "publish-canary": "pnpm --filter \"./packages/**\" -r publish --tag canary" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { - "@changesets/cli": "^2.25.2" + "@changesets/cli": "^2.26.0" } } diff --git a/packages/runtime/LICENSE.md b/packages/LICENSE similarity index 100% rename from packages/runtime/LICENSE.md rename to packages/LICENSE diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 000000000..2104ff0eb --- /dev/null +++ b/packages/README.md @@ -0,0 +1,122 @@ + + +## What it is + +ZenStack is a toolkit that simplifies the development of a web app's backend. It supercharges [Prisma ORM](https://prisma.io) with a powerful access control layer and unleashes its full potential for web development. + +Our goal is to let you save time writing boilerplate code and focus on building real features! + +## How it works + +ZenStack extended Prisma schema language for supporting custom attributes and functions and, based on that, implemented a flexible access control layer around Prisma. + +```prisma +// schema.zmodel + +model Post { + id String @id + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String + + // 🔐 allow logged-in users to read published posts + @@allow('read', auth() != null && published) + + // 🔐 allow full CRUD by author + @@allow('all', author == auth()) +} +``` + +At runtime, transparent proxies are created around Prisma clients for intercepting queries and mutations to enforce access policies. Moreover, framework integration packages help you wrap an access-control-enabled Prisma client into backend APIs that can be safely called from the frontend. + +```ts +// Next.js example: pages/api/model/[...path].ts + +import { requestHandler } from '@zenstackhq/next'; +import { withPolicy } from '@zenstackhq/runtime'; +import { getSessionUser } from '@lib/auth'; +import { prisma } from '@lib/db'; + +export default requestHandler({ + getPrisma: (req, res) => withPolicy(prisma, { user: getSessionUser(req, res) }), +}); +``` + +Plugins can generate strong-typed client libraries that talk to the APIs: + +```tsx +// React example: components/MyPosts.tsx + +import { usePost } from '@lib/hooks'; + +const MyPosts = () => { + // Post CRUD hooks + const { findMany } = usePost(); + + // list all posts that're visible to the current user, together with their authors + const { data: posts } = findMany({ + include: { author: true }, + orderBy: { createdAt: 'desc' }, + }); + + return ( +
    + {posts?.map((post) => ( +
  • + {post.title} by {post.author.name} +
  • + ))} +
+ ); +}; +``` + +## Links + +- [Home](https://zenstack.dev) +- [Documentation](https://zenstack.dev/docs) +- [Community chat](https://go.zenstack.dev/chat) +- [Twitter](https://twitter.com/zenstackhq) +- [Blog](https://dev.to/zenstack) + +## Features + +- Access control and data validation rules right inside your Prisma schema +- Auto-generated RESTful API and client library +- End-to-end type safety +- Extensible: custom attributes, functions, and a plugin system +- Framework agnostic +- Uncompromised performance + +## Examples + +Check out the [Collaborative Todo App](https://zenstack-todo.vercel.app/) for a running example. You can find the source code below: + +- [Next.js + React hooks implementation](https://github.com/zenstackhq/sample-todo-nextjs) +- [Next.js + tRPC implementation](https://github.com/zenstackhq/sample-todo-trpc) + +## Community + +Join our [discord server](https://go.zenstack.dev/chat) for chat and updates! + +## License + +[MIT](LICENSE) diff --git a/packages/language/.eslintrc.json b/packages/language/.eslintrc.json new file mode 100644 index 000000000..fc678164d --- /dev/null +++ b/packages/language/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "ignorePatterns": "src/generated/*", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/packages/language/LICENSE b/packages/language/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/packages/language/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/language/README.md b/packages/language/README.md new file mode 100644 index 000000000..8cad987c3 --- /dev/null +++ b/packages/language/README.md @@ -0,0 +1 @@ +ZenStack ZModel language compiler diff --git a/packages/schema/langium-config.json b/packages/language/langium-config.json similarity index 70% rename from packages/schema/langium-config.json rename to packages/language/langium-config.json index 568072312..4c20e0dcc 100644 --- a/packages/schema/langium-config.json +++ b/packages/language/langium-config.json @@ -3,12 +3,12 @@ "languages": [ { "id": "zmodel", - "grammar": "src/language-server/zmodel.langium", + "grammar": "src/zmodel.langium", "fileExtensions": [".zmodel"], "textMate": { "out": "syntaxes/zmodel.tmLanguage.json" } } ], - "out": "src/language-server/generated" + "out": "src/generated" } diff --git a/packages/language/package.json b/packages/language/package.json new file mode 100644 index 000000000..a7da6be48 --- /dev/null +++ b/packages/language/package.json @@ -0,0 +1,32 @@ +{ + "name": "@zenstackhq/language", + "version": "1.0.0-alpha.27", + "displayName": "ZenStack modeling language compiler", + "description": "ZenStack modeling language compiler", + "homepage": "https://zenstack.dev", + "scripts": { + "clean": "rimraf dist", + "generate": "langium generate", + "watch": "concurrently \"langium generate --watch\" \"tsc --watch\"", + "lint": "eslint src --ext ts", + "build": "pnpm lint && pnpm clean && pnpm generate && tsc && copyfiles -F ./README.md ./LICENSE ./package.json dist", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm build && pnpm publish --tag dev" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, + "author": "ZenStack Team", + "license": "MIT", + "devDependencies": { + "concurrently": "^7.4.0", + "copyfiles": "^2.4.1", + "langium-cli": "^1.0.0", + "rimraf": "^3.0.2", + "typescript": "^4.9.4" + }, + "dependencies": { + "langium": "^1.0.1" + } +} diff --git a/packages/language/src/ast.ts b/packages/language/src/ast.ts new file mode 100644 index 000000000..b58e2c737 --- /dev/null +++ b/packages/language/src/ast.ts @@ -0,0 +1,53 @@ +import { AbstractDeclaration, ExpressionType, BinaryExpr } from './generated/ast'; + +export * from './generated/ast'; +export { AstNode, Reference } from 'langium'; + +/** + * Shape of type resolution result: an expression type or reference to a declaration + */ +export type ResolvedShape = ExpressionType | AbstractDeclaration; + +/** + * Resolved type information (attached to expressions by linker) + */ +export type ResolvedType = { + decl?: ResolvedShape; + array?: boolean; +}; + +export const BinaryExprOperatorPriority: Record = { + //LogicalExpr + '||': 1, + '&&': 1, + //EqualityExpr + '==': 2, + '!=': 2, + //ComparisonExpr + '>': 3, + '<': 3, + '>=': 3, + '<=': 3, + //CollectionPredicateExpr + '^': 4, + '?': 4, + '!': 4, +}; + +declare module './generated/ast' { + interface AttributeArg { + /** + * Resolved attribute param declaration + */ + $resolvedParam?: AttributeParam; + } +} + +declare module 'langium' { + export interface AstNode { + /** + * Resolved type information attached to expressions + */ + $resolvedType?: ResolvedType; + } +} diff --git a/packages/schema/src/language-server/generated/ast.ts b/packages/language/src/generated/ast.ts similarity index 71% rename from packages/schema/src/language-server/generated/ast.ts rename to packages/language/src/generated/ast.ts index 9d7ec5a01..758d5eca3 100644 --- a/packages/schema/src/language-server/generated/ast.ts +++ b/packages/language/src/generated/ast.ts @@ -1,13 +1,12 @@ /****************************************************************************** - * This file was generated by langium-cli 0.5.0. + * This file was generated by langium-cli 1.0.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ -/* eslint-disable @typescript-eslint/array-type */ -/* eslint-disable @typescript-eslint/no-empty-interface */ -import { AstNode, AstReflection, Reference, ReferenceInfo, isAstNode, TypeMetaData } from 'langium'; +/* eslint-disable */ +import { AstNode, AbstractAstReflection, Reference, ReferenceInfo, TypeMetaData } from 'langium'; -export type AbstractDeclaration = Attribute | DataModel | DataSource | Enum | Function; +export type AbstractDeclaration = Attribute | DataModel | DataSource | Enum | FunctionDecl | GeneratorDecl | Plugin; export const AbstractDeclaration = 'AbstractDeclaration'; @@ -35,6 +34,8 @@ export function isExpression(item: unknown): item is Expression { export type ExpressionType = 'Any' | 'Boolean' | 'DateTime' | 'Float' | 'Int' | 'Null' | 'String'; +export type QualifiedName = string; + export type ReferenceTarget = DataModelField | EnumField | FunctionParam; export const ReferenceTarget = 'ReferenceTarget'; @@ -53,6 +54,7 @@ export function isTypeDeclaration(item: unknown): item is TypeDeclaration { export interface Argument extends AstNode { readonly $container: InvocationExpr; + readonly $type: 'Argument'; name?: string value: Expression } @@ -64,7 +66,8 @@ export function isArgument(item: unknown): item is Argument { } export interface ArrayExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'ArrayExpr'; items: Array } @@ -76,6 +79,7 @@ export function isArrayExpr(item: unknown): item is ArrayExpr { export interface Attribute extends AstNode { readonly $container: Model; + readonly $type: 'Attribute'; attributes: Array name: AttributeName params: Array @@ -89,6 +93,7 @@ export function isAttribute(item: unknown): item is Attribute { export interface AttributeArg extends AstNode { readonly $container: AttributeAttribute | DataModelAttribute | DataModelFieldAttribute; + readonly $type: 'AttributeArg'; name?: string value: Expression } @@ -101,6 +106,7 @@ export function isAttributeArg(item: unknown): item is AttributeArg { export interface AttributeAttribute extends AstNode { readonly $container: Attribute; + readonly $type: 'AttributeAttribute'; args: Array decl: Reference } @@ -113,6 +119,7 @@ export function isAttributeAttribute(item: unknown): item is AttributeAttribute export interface AttributeParam extends AstNode { readonly $container: Attribute; + readonly $type: 'AttributeParam'; default: boolean name: string type: AttributeParamType @@ -126,10 +133,11 @@ export function isAttributeParam(item: unknown): item is AttributeParam { export interface AttributeParamType extends AstNode { readonly $container: AttributeParam; + readonly $type: 'AttributeParamType'; array: boolean optional: boolean reference?: Reference - type?: 'ContextType' | 'FieldReference' | ExpressionType + type?: 'ContextType' | 'FieldReference' | 'TransitiveFieldReference' | ExpressionType } export const AttributeParamType = 'AttributeParamType'; @@ -139,7 +147,8 @@ export function isAttributeParamType(item: unknown): item is AttributeParamType } export interface BinaryExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'BinaryExpr'; left: Expression operator: '!' | '!=' | '&&' | '<' | '<=' | '==' | '>' | '>=' | '?' | '^' | '||' right: Expression @@ -153,7 +162,9 @@ export function isBinaryExpr(item: unknown): item is BinaryExpr { export interface DataModel extends AstNode { readonly $container: Model; + readonly $type: 'DataModel'; attributes: Array + comments: Array fields: Array name: string } @@ -166,6 +177,7 @@ export function isDataModel(item: unknown): item is DataModel { export interface DataModelAttribute extends AstNode { readonly $container: DataModel; + readonly $type: 'DataModelAttribute'; args: Array decl: Reference } @@ -177,8 +189,10 @@ export function isDataModelAttribute(item: unknown): item is DataModelAttribute } export interface DataModelField extends AstNode { - readonly $container: DataModel; + readonly $container: DataModel | Enum | FunctionDecl; + readonly $type: 'DataModelField'; attributes: Array + comments: Array name: string type: DataModelFieldType } @@ -191,6 +205,7 @@ export function isDataModelField(item: unknown): item is DataModelField { export interface DataModelFieldAttribute extends AstNode { readonly $container: DataModelField; + readonly $type: 'DataModelFieldAttribute'; args: Array decl: Reference } @@ -203,6 +218,7 @@ export function isDataModelFieldAttribute(item: unknown): item is DataModelField export interface DataModelFieldType extends AstNode { readonly $container: DataModelField; + readonly $type: 'DataModelFieldType'; array: boolean optional: boolean reference?: Reference @@ -217,6 +233,7 @@ export function isDataModelFieldType(item: unknown): item is DataModelFieldType export interface DataSource extends AstNode { readonly $container: Model; + readonly $type: 'DataSource'; fields: Array name: string } @@ -229,6 +246,7 @@ export function isDataSource(item: unknown): item is DataSource { export interface DataSourceField extends AstNode { readonly $container: DataSource; + readonly $type: 'DataSourceField'; name: string value: InvocationExpr | LiteralExpr } @@ -241,6 +259,7 @@ export function isDataSourceField(item: unknown): item is DataSourceField { export interface Enum extends AstNode { readonly $container: Model; + readonly $type: 'Enum'; fields: Array name: string } @@ -252,7 +271,8 @@ export function isEnum(item: unknown): item is Enum { } export interface EnumField extends AstNode { - readonly $container: Enum; + readonly $container: DataModel | Enum | FunctionDecl; + readonly $type: 'EnumField'; name: string } @@ -262,22 +282,24 @@ export function isEnumField(item: unknown): item is EnumField { return reflection.isInstance(item, EnumField); } -export interface Function extends AstNode { +export interface FunctionDecl extends AstNode { readonly $container: Model; + readonly $type: 'FunctionDecl'; expression?: Expression name: string params: Array returnType: FunctionParamType } -export const Function = 'Function'; +export const FunctionDecl = 'FunctionDecl'; -export function isFunction(item: unknown): item is Function { - return reflection.isInstance(item, Function); +export function isFunctionDecl(item: unknown): item is FunctionDecl { + return reflection.isInstance(item, FunctionDecl); } export interface FunctionParam extends AstNode { - readonly $container: Function; + readonly $container: DataModel | Enum | FunctionDecl; + readonly $type: 'FunctionParam'; name: string type: FunctionParamType } @@ -289,7 +311,8 @@ export function isFunctionParam(item: unknown): item is FunctionParam { } export interface FunctionParamType extends AstNode { - readonly $container: Function | FunctionParam; + readonly $container: FunctionDecl | FunctionParam; + readonly $type: 'FunctionParamType'; array: boolean reference?: Reference type?: ExpressionType @@ -301,10 +324,37 @@ export function isFunctionParamType(item: unknown): item is FunctionParamType { return reflection.isInstance(item, FunctionParamType); } +export interface GeneratorDecl extends AstNode { + readonly $container: Model; + readonly $type: 'GeneratorDecl'; + fields: Array + name: string +} + +export const GeneratorDecl = 'GeneratorDecl'; + +export function isGeneratorDecl(item: unknown): item is GeneratorDecl { + return reflection.isInstance(item, GeneratorDecl); +} + +export interface GeneratorField extends AstNode { + readonly $container: GeneratorDecl; + readonly $type: 'GeneratorField'; + name: string + value: ArrayExpr | LiteralExpr +} + +export const GeneratorField = 'GeneratorField'; + +export function isGeneratorField(item: unknown): item is GeneratorField { + return reflection.isInstance(item, GeneratorField); +} + export interface InvocationExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'InvocationExpr'; args: Array - function: Reference + function: Reference } export const InvocationExpr = 'InvocationExpr'; @@ -314,7 +364,8 @@ export function isInvocationExpr(item: unknown): item is InvocationExpr { } export interface LiteralExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'LiteralExpr'; value: boolean | number | string } @@ -325,7 +376,8 @@ export function isLiteralExpr(item: unknown): item is LiteralExpr { } export interface MemberAccessExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'MemberAccessExpr'; member: Reference operand: Expression } @@ -337,6 +389,7 @@ export function isMemberAccessExpr(item: unknown): item is MemberAccessExpr { } export interface Model extends AstNode { + readonly $type: 'Model'; declarations: Array } @@ -347,7 +400,8 @@ export function isModel(item: unknown): item is Model { } export interface NullExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'NullExpr'; value: string } @@ -357,8 +411,35 @@ export function isNullExpr(item: unknown): item is NullExpr { return reflection.isInstance(item, NullExpr); } +export interface Plugin extends AstNode { + readonly $container: Model; + readonly $type: 'Plugin'; + fields: Array + name: string +} + +export const Plugin = 'Plugin'; + +export function isPlugin(item: unknown): item is Plugin { + return reflection.isInstance(item, Plugin); +} + +export interface PluginField extends AstNode { + readonly $container: Plugin; + readonly $type: 'PluginField'; + name: string + value: ArrayExpr | LiteralExpr +} + +export const PluginField = 'PluginField'; + +export function isPluginField(item: unknown): item is PluginField { + return reflection.isInstance(item, PluginField); +} + export interface ReferenceArg extends AstNode { readonly $container: ReferenceExpr; + readonly $type: 'ReferenceArg'; name: 'sort' value: 'Asc' | 'Desc' } @@ -370,7 +451,8 @@ export function isReferenceArg(item: unknown): item is ReferenceArg { } export interface ReferenceExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'ReferenceExpr'; args: Array target: Reference } @@ -382,7 +464,8 @@ export function isReferenceExpr(item: unknown): item is ReferenceExpr { } export interface ThisExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'ThisExpr'; value: string } @@ -393,7 +476,8 @@ export function isThisExpr(item: unknown): item is ThisExpr { } export interface UnaryExpr extends AstNode { - readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | Function | MemberAccessExpr | UnaryExpr; + readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; + readonly $type: 'UnaryExpr'; operand: Expression operator: '!' } @@ -404,22 +488,53 @@ export function isUnaryExpr(item: unknown): item is UnaryExpr { return reflection.isInstance(item, UnaryExpr); } -export type ZModelAstType = 'AbstractDeclaration' | 'Argument' | 'ArrayExpr' | 'Attribute' | 'AttributeArg' | 'AttributeAttribute' | 'AttributeParam' | 'AttributeParamType' | 'BinaryExpr' | 'DataModel' | 'DataModelAttribute' | 'DataModelField' | 'DataModelFieldAttribute' | 'DataModelFieldType' | 'DataSource' | 'DataSourceField' | 'Enum' | 'EnumField' | 'Expression' | 'Function' | 'FunctionParam' | 'FunctionParamType' | 'InvocationExpr' | 'LiteralExpr' | 'MemberAccessExpr' | 'Model' | 'NullExpr' | 'ReferenceArg' | 'ReferenceExpr' | 'ReferenceTarget' | 'ThisExpr' | 'TypeDeclaration' | 'UnaryExpr'; - -export class ZModelAstReflection implements AstReflection { +export interface ZModelAstType { + AbstractDeclaration: AbstractDeclaration + Argument: Argument + ArrayExpr: ArrayExpr + Attribute: Attribute + AttributeArg: AttributeArg + AttributeAttribute: AttributeAttribute + AttributeParam: AttributeParam + AttributeParamType: AttributeParamType + BinaryExpr: BinaryExpr + DataModel: DataModel + DataModelAttribute: DataModelAttribute + DataModelField: DataModelField + DataModelFieldAttribute: DataModelFieldAttribute + DataModelFieldType: DataModelFieldType + DataSource: DataSource + DataSourceField: DataSourceField + Enum: Enum + EnumField: EnumField + Expression: Expression + FunctionDecl: FunctionDecl + FunctionParam: FunctionParam + FunctionParamType: FunctionParamType + GeneratorDecl: GeneratorDecl + GeneratorField: GeneratorField + InvocationExpr: InvocationExpr + LiteralExpr: LiteralExpr + MemberAccessExpr: MemberAccessExpr + Model: Model + NullExpr: NullExpr + Plugin: Plugin + PluginField: PluginField + ReferenceArg: ReferenceArg + ReferenceExpr: ReferenceExpr + ReferenceTarget: ReferenceTarget + ThisExpr: ThisExpr + TypeDeclaration: TypeDeclaration + UnaryExpr: UnaryExpr +} + +export class ZModelAstReflection extends AbstractAstReflection { getAllTypes(): string[] { - return ['AbstractDeclaration', 'Argument', 'ArrayExpr', 'Attribute', 'AttributeArg', 'AttributeAttribute', 'AttributeParam', 'AttributeParamType', 'BinaryExpr', 'DataModel', 'DataModelAttribute', 'DataModelField', 'DataModelFieldAttribute', 'DataModelFieldType', 'DataSource', 'DataSourceField', 'Enum', 'EnumField', 'Expression', 'Function', 'FunctionParam', 'FunctionParamType', 'InvocationExpr', 'LiteralExpr', 'MemberAccessExpr', 'Model', 'NullExpr', 'ReferenceArg', 'ReferenceExpr', 'ReferenceTarget', 'ThisExpr', 'TypeDeclaration', 'UnaryExpr']; - } - - isInstance(node: unknown, type: string): boolean { - return isAstNode(node) && this.isSubtype(node.$type, type); + return ['AbstractDeclaration', 'Argument', 'ArrayExpr', 'Attribute', 'AttributeArg', 'AttributeAttribute', 'AttributeParam', 'AttributeParamType', 'BinaryExpr', 'DataModel', 'DataModelAttribute', 'DataModelField', 'DataModelFieldAttribute', 'DataModelFieldType', 'DataSource', 'DataSourceField', 'Enum', 'EnumField', 'Expression', 'FunctionDecl', 'FunctionParam', 'FunctionParamType', 'GeneratorDecl', 'GeneratorField', 'InvocationExpr', 'LiteralExpr', 'MemberAccessExpr', 'Model', 'NullExpr', 'Plugin', 'PluginField', 'ReferenceArg', 'ReferenceExpr', 'ReferenceTarget', 'ThisExpr', 'TypeDeclaration', 'UnaryExpr']; } - isSubtype(subtype: string, supertype: string): boolean { - if (subtype === supertype) { - return true; - } + protected override computeIsSubtype(subtype: string, supertype: string): boolean { switch (subtype) { case ArrayExpr: case BinaryExpr: @@ -434,7 +549,9 @@ export class ZModelAstReflection implements AstReflection { } case Attribute: case DataSource: - case Function: { + case FunctionDecl: + case GeneratorDecl: + case Plugin: { return this.isSubtype(AbstractDeclaration, supertype); } case DataModel: @@ -455,26 +572,18 @@ export class ZModelAstReflection implements AstReflection { getReferenceType(refInfo: ReferenceInfo): string { const referenceId = `${refInfo.container.$type}:${refInfo.property}`; switch (referenceId) { - case 'AttributeAttribute:decl': { - return Attribute; - } - case 'AttributeParamType:reference': { - return TypeDeclaration; - } - case 'DataModelAttribute:decl': { - return Attribute; - } + case 'AttributeAttribute:decl': + case 'DataModelAttribute:decl': case 'DataModelFieldAttribute:decl': { return Attribute; } - case 'DataModelFieldType:reference': { - return TypeDeclaration; - } + case 'AttributeParamType:reference': + case 'DataModelFieldType:reference': case 'FunctionParamType:reference': { return TypeDeclaration; } case 'InvocationExpr:function': { - return Function; + return FunctionDecl; } case 'MemberAccessExpr:member': { return DataModelField; @@ -537,6 +646,7 @@ export class ZModelAstReflection implements AstReflection { name: 'DataModel', mandatory: [ { name: 'attributes', type: 'array' }, + { name: 'comments', type: 'array' }, { name: 'fields', type: 'array' } ] }; @@ -553,7 +663,8 @@ export class ZModelAstReflection implements AstReflection { return { name: 'DataModelField', mandatory: [ - { name: 'attributes', type: 'array' } + { name: 'attributes', type: 'array' }, + { name: 'comments', type: 'array' } ] }; } @@ -590,9 +701,9 @@ export class ZModelAstReflection implements AstReflection { ] }; } - case 'Function': { + case 'FunctionDecl': { return { - name: 'Function', + name: 'FunctionDecl', mandatory: [ { name: 'params', type: 'array' } ] @@ -606,6 +717,14 @@ export class ZModelAstReflection implements AstReflection { ] }; } + case 'GeneratorDecl': { + return { + name: 'GeneratorDecl', + mandatory: [ + { name: 'fields', type: 'array' } + ] + }; + } case 'InvocationExpr': { return { name: 'InvocationExpr', @@ -622,6 +741,14 @@ export class ZModelAstReflection implements AstReflection { ] }; } + case 'Plugin': { + return { + name: 'Plugin', + mandatory: [ + { name: 'fields', type: 'array' } + ] + }; + } case 'ReferenceExpr': { return { name: 'ReferenceExpr', diff --git a/packages/schema/src/language-server/generated/grammar.ts b/packages/language/src/generated/grammar.ts similarity index 83% rename from packages/schema/src/language-server/generated/grammar.ts rename to packages/language/src/generated/grammar.ts index 6ab93eb19..d82b57737 100644 --- a/packages/schema/src/language-server/generated/grammar.ts +++ b/packages/language/src/generated/grammar.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 0.5.0. + * This file was generated by langium-cli 1.0.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ @@ -22,7 +22,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AbstractDeclaration" + "$ref": "#/rules@1" }, "arguments": [] }, @@ -43,35 +43,49 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "DataSource" + "$ref": "#/rules@2" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "DataModel" + "$ref": "#/rules@4" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "Enum" + "$ref": "#/rules@6" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "Function" + "$ref": "#/rules@26" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "Attribute" + "$ref": "#/rules@29" + }, + "arguments": [] + }, + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@31" + }, + "arguments": [] + }, + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@39" }, "arguments": [] } @@ -101,7 +115,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -117,7 +131,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataSourceField" + "$ref": "#/rules@3" }, "arguments": [] }, @@ -149,7 +163,114 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" + }, + "arguments": [] + } + }, + { + "$type": "Keyword", + "value": "=" + }, + { + "$type": "Assignment", + "feature": "value", + "operator": "=", + "terminal": { + "$type": "Alternatives", + "elements": [ + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@9" + }, + "arguments": [] + }, + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@16" + }, + "arguments": [] + } + ] + } + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "GeneratorDecl", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "generator" + }, + { + "$type": "Assignment", + "feature": "name", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" + }, + "arguments": [] + } + }, + { + "$type": "Keyword", + "value": "{" + }, + { + "$type": "Assignment", + "feature": "fields", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@5" + }, + "arguments": [] + }, + "cardinality": "*" + }, + { + "$type": "Keyword", + "value": "}" + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "GeneratorField", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Assignment", + "feature": "name", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" }, "arguments": [] } @@ -168,14 +289,121 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "LiteralExpr" + "$ref": "#/rules@9" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "InvocationExpr" + "$ref": "#/rules@10" + }, + "arguments": [] + } + ] + } + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "Plugin", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "plugin" + }, + { + "$type": "Assignment", + "feature": "name", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" + }, + "arguments": [] + } + }, + { + "$type": "Keyword", + "value": "{" + }, + { + "$type": "Assignment", + "feature": "fields", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@7" + }, + "arguments": [] + }, + "cardinality": "*" + }, + { + "$type": "Keyword", + "value": "}" + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "PluginField", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Assignment", + "feature": "name", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" + }, + "arguments": [] + } + }, + { + "$type": "Keyword", + "value": "=" + }, + { + "$type": "Assignment", + "feature": "value", + "operator": "=", + "terminal": { + "$type": "Alternatives", + "elements": [ + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@9" + }, + "arguments": [] + }, + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@10" }, "arguments": [] } @@ -197,7 +425,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "definition": { "$type": "RuleCall", "rule": { - "$refText": "LogicalExpr" + "$ref": "#/rules@22" }, "arguments": [] }, @@ -221,21 +449,21 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "BOOLEAN" + "$ref": "#/rules@50" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "NUMBER" + "$ref": "#/rules@55" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "STRING" + "$ref": "#/rules@54" }, "arguments": [] } @@ -269,7 +497,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -288,7 +516,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -322,7 +550,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "THIS" + "$ref": "#/rules@52" }, "arguments": [] } @@ -344,7 +572,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "NULL" + "$ref": "#/rules@51" }, "arguments": [] } @@ -369,12 +597,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "ReferenceTarget" + "$ref": "#/types@0" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] }, @@ -391,7 +619,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ReferenceArgList" + "$ref": "#/rules@14" }, "arguments": [] }, @@ -425,7 +653,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ReferenceArg" + "$ref": "#/rules@15" }, "arguments": [] } @@ -444,7 +672,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ReferenceArg" + "$ref": "#/rules@15" }, "arguments": [] } @@ -519,7 +747,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "Function" + "$ref": "#/rules@31" }, "deprecatedSyntax": false } @@ -531,7 +759,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ArgumentList" + "$ref": "#/rules@24" }, "arguments": [], "cardinality": "?" @@ -571,7 +799,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -598,7 +826,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "PrimaryExpr" + "$ref": "#/rules@23" }, "arguments": [] }, @@ -628,7 +856,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "DataModelField" + "$ref": "#/rules@27" }, "deprecatedSyntax": false } @@ -660,7 +888,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "MemberAccessExpr" + "$ref": "#/rules@18" }, "arguments": [] }, @@ -709,7 +937,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -743,7 +971,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "CollectionPredicateExpr" + "$ref": "#/rules@19" }, "arguments": [] }, @@ -792,7 +1020,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "CollectionPredicateExpr" + "$ref": "#/rules@19" }, "arguments": [] } @@ -822,7 +1050,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ComparisonExpr" + "$ref": "#/rules@20" }, "arguments": [] }, @@ -863,7 +1091,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ComparisonExpr" + "$ref": "#/rules@20" }, "arguments": [] } @@ -893,7 +1121,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "EqualityExpr" + "$ref": "#/rules@21" }, "arguments": [] }, @@ -934,7 +1162,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "EqualityExpr" + "$ref": "#/rules@21" }, "arguments": [] } @@ -971,7 +1199,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] }, @@ -984,49 +1212,49 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ThisExpr" + "$ref": "#/rules@11" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "NullExpr" + "$ref": "#/rules@12" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "LiteralExpr" + "$ref": "#/rules@9" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "InvocationExpr" + "$ref": "#/rules@16" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "ArrayExpr" + "$ref": "#/rules@10" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "ReferenceExpr" + "$ref": "#/rules@13" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "UnaryExpr" + "$ref": "#/rules@17" }, "arguments": [] } @@ -1053,7 +1281,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Argument" + "$ref": "#/rules@25" }, "arguments": [] } @@ -1072,7 +1300,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Argument" + "$ref": "#/rules@25" }, "arguments": [] } @@ -1104,7 +1332,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1123,7 +1351,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -1143,6 +1371,19 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "definition": { "$type": "Group", "elements": [ + { + "$type": "Assignment", + "feature": "comments", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@56" + }, + "arguments": [] + }, + "cardinality": "*" + }, { "$type": "Keyword", "value": "model" @@ -1154,7 +1395,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1173,7 +1414,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelField" + "$ref": "#/rules@27" }, "arguments": [] } @@ -1185,7 +1426,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelAttribute" + "$ref": "#/rules@43" }, "arguments": [] } @@ -1212,6 +1453,19 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "definition": { "$type": "Group", "elements": [ + { + "$type": "Assignment", + "feature": "comments", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@56" + }, + "arguments": [] + }, + "cardinality": "*" + }, { "$type": "Assignment", "feature": "name", @@ -1219,7 +1473,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1231,7 +1485,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelFieldType" + "$ref": "#/rules@28" }, "arguments": [] } @@ -1243,7 +1497,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelFieldAttribute" + "$ref": "#/rules@42" }, "arguments": [] }, @@ -1274,7 +1528,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "BuiltinType" + "$ref": "#/rules@48" }, "arguments": [] } @@ -1286,12 +1540,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "TypeDeclaration" + "$ref": "#/types@1" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] }, @@ -1346,7 +1600,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1362,7 +1616,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "EnumField" + "$ref": "#/rules@30" }, "arguments": [] }, @@ -1391,7 +1645,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1405,7 +1659,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, { "$type": "ParserRule", - "name": "Function", + "name": "FunctionDecl", "definition": { "$type": "Group", "elements": [ @@ -1420,7 +1674,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1439,7 +1693,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "FunctionParam" + "$ref": "#/rules@32" }, "arguments": [] } @@ -1458,7 +1712,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "FunctionParam" + "$ref": "#/rules@32" }, "arguments": [] } @@ -1484,7 +1738,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "FunctionParamType" + "$ref": "#/rules@33" }, "arguments": [] } @@ -1500,7 +1754,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] }, @@ -1532,7 +1786,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1548,7 +1802,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "FunctionParamType" + "$ref": "#/rules@33" }, "arguments": [] } @@ -1578,7 +1832,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ExpressionType" + "$ref": "#/rules@47" }, "arguments": [] } @@ -1590,7 +1844,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "TypeDeclaration" + "$ref": "#/types@1" }, "deprecatedSyntax": false } @@ -1616,6 +1870,46 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "parameters": [], "wildcard": false }, + { + "$type": "ParserRule", + "name": "QualifiedName", + "dataType": "string", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" + }, + "arguments": [] + }, + { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "." + }, + { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@53" + }, + "arguments": [] + } + ], + "cardinality": "*" + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, { "$type": "ParserRule", "name": "AttributeAttributeName", @@ -1630,7 +1924,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@34" }, "arguments": [] } @@ -1657,7 +1951,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@34" }, "arguments": [] } @@ -1684,7 +1978,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@34" }, "arguments": [] } @@ -1707,21 +2001,21 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "DataModelAttributeName" + "$ref": "#/rules@36" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "DataModelFieldAttributeName" + "$ref": "#/rules@37" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$refText": "AttributeAttributeName" + "$ref": "#/rules@35" }, "arguments": [] } @@ -1751,7 +2045,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeName" + "$ref": "#/rules@38" }, "arguments": [] } @@ -1770,7 +2064,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeParam" + "$ref": "#/rules@40" }, "arguments": [] } @@ -1789,7 +2083,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeParam" + "$ref": "#/rules@40" }, "arguments": [] } @@ -1811,7 +2105,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeAttribute" + "$ref": "#/rules@44" }, "arguments": [] }, @@ -1849,7 +2143,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -1865,7 +2159,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeParamType" + "$ref": "#/rules@41" }, "arguments": [] } @@ -1898,7 +2192,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "ExpressionType" + "$ref": "#/rules@47" }, "arguments": [] }, @@ -1906,6 +2200,10 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "$type": "Keyword", "value": "FieldReference" }, + { + "$type": "Keyword", + "value": "TransitiveFieldReference" + }, { "$type": "Keyword", "value": "ContextType" @@ -1920,12 +2218,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "TypeDeclaration" + "$ref": "#/types@1" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] }, @@ -1976,12 +2274,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "Attribute" + "$ref": "#/rules@39" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelFieldAttributeName" + "$ref": "#/rules@37" }, "arguments": [] }, @@ -1998,7 +2296,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "AttributeArgList" + "$ref": "#/rules@45" }, "arguments": [], "cardinality": "?" @@ -2032,12 +2330,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "Attribute" + "$ref": "#/rules@39" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "DataModelAttributeName" + "$ref": "#/rules@36" }, "arguments": [] }, @@ -2054,7 +2352,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "AttributeArgList" + "$ref": "#/rules@45" }, "arguments": [], "cardinality": "?" @@ -2088,12 +2386,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$refText": "Attribute" + "$ref": "#/rules@39" }, "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeAttributeName" + "$ref": "#/rules@35" }, "arguments": [] }, @@ -2110,7 +2408,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$refText": "AttributeArgList" + "$ref": "#/rules@45" }, "arguments": [], "cardinality": "?" @@ -2145,7 +2443,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeArg" + "$ref": "#/rules@46" }, "arguments": [] } @@ -2164,7 +2462,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "AttributeArg" + "$ref": "#/rules@46" }, "arguments": [] } @@ -2196,7 +2494,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$ref": "#/rules@53" }, "arguments": [] } @@ -2215,7 +2513,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$refText": "Expression" + "$ref": "#/rules@8" }, "arguments": [] } @@ -2409,6 +2707,16 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "fragment": false, "hidden": false }, + { + "$type": "TerminalRule", + "name": "TRIPLE_SLASH_COMMENT", + "definition": { + "$type": "RegexToken", + "regex": "\\\\/\\\\/\\\\/[^\\\\n\\\\r]*" + }, + "fragment": false, + "hidden": false + }, { "$type": "TerminalRule", "hidden": true, @@ -2437,7 +2745,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "AtomType", "refType": { - "$refText": "FunctionParam" + "$ref": "#/rules@32" }, "isArray": false, "isRef": false @@ -2445,7 +2753,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "AtomType", "refType": { - "$refText": "DataModelField" + "$ref": "#/rules@27" }, "isArray": false, "isRef": false @@ -2453,7 +2761,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "AtomType", "refType": { - "$refText": "EnumField" + "$ref": "#/rules@30" }, "isArray": false, "isRef": false @@ -2467,7 +2775,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "AtomType", "refType": { - "$refText": "DataModel" + "$ref": "#/rules@26" }, "isArray": false, "isRef": false @@ -2475,7 +2783,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "AtomType", "refType": { - "$refText": "Enum" + "$ref": "#/rules@29" }, "isArray": false, "isRef": false diff --git a/packages/schema/src/language-server/generated/module.ts b/packages/language/src/generated/module.ts similarity index 95% rename from packages/schema/src/language-server/generated/module.ts rename to packages/language/src/generated/module.ts index 2ba861e07..e2111b145 100644 --- a/packages/schema/src/language-server/generated/module.ts +++ b/packages/language/src/generated/module.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 0.5.0. + * This file was generated by langium-cli 1.0.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ diff --git a/packages/language/src/module.ts b/packages/language/src/module.ts new file mode 100644 index 000000000..81d5c9b06 --- /dev/null +++ b/packages/language/src/module.ts @@ -0,0 +1 @@ +export * from './generated/module'; diff --git a/packages/schema/src/language-server/zmodel.langium b/packages/language/src/zmodel.langium similarity index 82% rename from packages/schema/src/language-server/zmodel.langium rename to packages/language/src/zmodel.langium index ef6ba05e0..681608f3c 100644 --- a/packages/schema/src/language-server/zmodel.langium +++ b/packages/language/src/zmodel.langium @@ -6,14 +6,28 @@ entry Model: )*; AbstractDeclaration: - DataSource | DataModel | Enum | Function | Attribute; + DataSource | GeneratorDecl| Plugin | DataModel | Enum | FunctionDecl | Attribute; // datasource DataSource: 'datasource' name=ID '{' (fields+=DataSourceField)* '}'; DataSourceField: - (name=ID '=' value=(LiteralExpr|InvocationExpr)); + name=ID '=' value=(LiteralExpr|InvocationExpr); + +// generator +GeneratorDecl: + 'generator' name=ID '{' (fields+=GeneratorField)* '}'; + +GeneratorField: + name=ID '=' value=(LiteralExpr | ArrayExpr); + +// plugin +Plugin: + 'plugin' name=ID '{' (fields+=PluginField)* '}'; + +PluginField: + name=ID '=' value=(LiteralExpr | ArrayExpr); // expression Expression: @@ -43,7 +57,7 @@ ReferenceArg: name=('sort') ':' value=('Asc'| 'Desc'); InvocationExpr: - function=[Function] '(' ArgumentList? ')'; + function=[FunctionDecl] '(' ArgumentList? ')'; UnaryExpr: operator=('!') operand=Expression; @@ -119,6 +133,7 @@ Argument: // model DataModel: + (comments+=TRIPLE_SLASH_COMMENT)* 'model' name=ID '{' ( fields+=DataModelField | attributes+=DataModelAttribute @@ -126,6 +141,7 @@ DataModel: '}'; DataModelField: + (comments+=TRIPLE_SLASH_COMMENT)* name=ID type=DataModelFieldType (attributes+=DataModelFieldAttribute)*; DataModelFieldType: @@ -139,7 +155,7 @@ EnumField: name=ID; // function -Function: +FunctionDecl: 'function' name=ID '(' (params+=FunctionParam (',' params+=FunctionParam)*)? ')' ':' returnType=FunctionParamType '{' (expression=Expression)? '}'; FunctionParam: @@ -148,17 +164,20 @@ FunctionParam: FunctionParamType: (type=ExpressionType | reference=[TypeDeclaration]) (array?='[]')?; +QualifiedName returns string: + ID ('.' ID)*; + // attribute-level attribute AttributeAttributeName returns string: - '@@@' ID; + '@@@' QualifiedName; // model-level attribute DataModelAttributeName returns string: - '@@' ID; + '@@' QualifiedName; // field-level attribute DataModelFieldAttributeName returns string: - '@' ID; + '@' QualifiedName; AttributeName returns string: DataModelAttributeName | DataModelFieldAttributeName | AttributeAttributeName; @@ -170,8 +189,10 @@ Attribute: AttributeParam: (default?='_')? name=ID ':' type=AttributeParamType; +// FieldReference refers to fields declared in the current model +// TransitiveFieldReference refers to fields declared in the model type of the current field AttributeParamType: - (type=(ExpressionType | 'FieldReference' | 'ContextType') | reference=[TypeDeclaration:ID]) (array?='[]')? (optional?='?')?; + (type=(ExpressionType | 'FieldReference' | 'TransitiveFieldReference' | 'ContextType') | reference=[TypeDeclaration:ID]) (array?='[]')? (optional?='?')?; type TypeDeclaration = DataModel | Enum; @@ -203,5 +224,6 @@ terminal THIS: 'this'; terminal ID: /[_a-zA-Z][\w_]*/; terminal STRING: /"[^"]*"|'[^']*'/; terminal NUMBER returns number: /[+-]?[0-9]+(\.[0-9]+)?/; +terminal TRIPLE_SLASH_COMMENT: /\/\/\/[^\n\r]*/; hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; diff --git a/packages/language/syntaxes/zmodel.tmLanguage.json b/packages/language/syntaxes/zmodel.tmLanguage.json new file mode 100644 index 000000000..820258417 --- /dev/null +++ b/packages/language/syntaxes/zmodel.tmLanguage.json @@ -0,0 +1,57 @@ +{ + "name": "zmodel", + "scopeName": "source.zmodel", + "fileTypes": [ + ".zmodel" + ], + "patterns": [ + { + "include": "#comments" + }, + { + "name": "keyword.control.zmodel", + "match": "\\b(Any|Asc|attribute|BigInt|Boolean|Bytes|ContextType|datasource|DateTime|Decimal|Desc|enum|FieldReference|Float|function|generator|Int|Json|model|Null|plugin|sort|String|TransitiveFieldReference)\\b" + }, + { + "name": "string.quoted.double.zmodel", + "begin": "\"", + "end": "\"" + }, + { + "name": "string.quoted.single.zmodel", + "begin": "'", + "end": "'" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.block.zmodel", + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + } + }, + { + "begin": "//", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.zmodel" + } + }, + "end": "(?=$)", + "name": "comment.line.zmodel" + } + ] + } + } +} \ No newline at end of file diff --git a/packages/next-auth/tsconfig.json b/packages/language/tsconfig.json similarity index 100% rename from packages/next-auth/tsconfig.json rename to packages/language/tsconfig.json diff --git a/packages/next-auth/README.md b/packages/next-auth/README.md deleted file mode 100644 index b9ae1045b..000000000 --- a/packages/next-auth/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# ZenStack Runtime Library - -This package is for integrating [next-auth](https://next-auth.js.org/) with ZenStack. - -Visit [Homepage](https://zenstack.dev) for more details. diff --git a/packages/next-auth/package.json b/packages/next-auth/package.json deleted file mode 100644 index 684f0a78a..000000000 --- a/packages/next-auth/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@zenstackhq/next-auth", - "displayName": "ZenStack next-auth integration library", - "version": "0.5.0", - "description": "ZenStack adapter for integrating with next-auth", - "repository": { - "type": "git", - "url": "https://github.com/zenstackhq/zenstack" - }, - "main": "index.js", - "scripts": { - "clean": "rimraf dist", - "build": "pnpm lint && pnpm clean && tsc && cp ./package.json ./README.md dist/", - "watch": "tsc --watch", - "lint": "eslint src --ext ts", - "prepublishOnly": "pnpm build", - "publish-dev": "pnpm publish --tag dev" - }, - "publishConfig": { - "directory": "dist", - "linkDirectory": true - }, - "author": { - "name": "ZenStack Team" - }, - "homepage": "https://zenstack.dev", - "license": "MIT", - "keywords": [ - "zenstack", - "next-auth" - ], - "dependencies": { - "@next-auth/prisma-adapter": "^1.0.5", - "@zenstackhq/runtime": "workspace:*", - "bcryptjs": "^2.4.3" - }, - "devDependencies": { - "@types/bcryptjs": "^2.4.2", - "next-auth": "^4.0.0", - "rimraf": "^3.0.2", - "typescript": "^4.9.3" - }, - "peerDependencies": { - "next-auth": "^4.0.0" - } -} diff --git a/packages/next-auth/src/adapter.ts b/packages/next-auth/src/adapter.ts deleted file mode 100644 index 9b347eeaa..000000000 --- a/packages/next-auth/src/adapter.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Adapter } from 'next-auth/adapters'; -import type { Service } from '@zenstackhq/runtime/server'; -import { PrismaAdapter } from '@next-auth/prisma-adapter'; - -/** - * Next-auth adapter for reading and persisting auth entities - * @param service ZenStack service - */ -export function Adapter(service: Service): Adapter { - return PrismaAdapter(service.db); -} diff --git a/packages/next-auth/src/authorize.ts b/packages/next-auth/src/authorize.ts deleted file mode 100644 index 0f8e4ddc2..000000000 --- a/packages/next-auth/src/authorize.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Service } from '@zenstackhq/runtime/server'; -import { compare } from 'bcryptjs'; - -async function verifyPassword(password: string, hashedPassword: string) { - return compare(password, hashedPassword); -} - -export class AuthorizeError extends Error { - constructor(message: string) { - super(message); - } -} - -export function authorize(service: Service) { - return async ( - credentials: Record<'email' | 'password', string> | undefined - ) => { - if (!credentials) { - throw new AuthorizeError('Missing credentials'); - } - - if (!credentials.email) { - throw new AuthorizeError('"email" is required in credentials'); - } - - if (!credentials.password) { - throw new AuthorizeError('"password" is required in credentials'); - } - - const maybeUser = await service.db.user.findFirst({ - where: { - email: credentials.email, - }, - select: { - id: true, - email: true, - password: true, - }, - }); - - if (!maybeUser || !maybeUser.password) { - return null; - } - - const isValid = await verifyPassword( - credentials.password, - maybeUser.password - ); - - if (!isValid) { - return null; - } - - return { - id: maybeUser.id, - email: maybeUser.email, - }; - }; -} diff --git a/packages/next-auth/src/index.ts b/packages/next-auth/src/index.ts deleted file mode 100644 index 802ff07f9..000000000 --- a/packages/next-auth/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './adapter'; -export * from './authorize'; diff --git a/packages/next-auth/.eslintrc.json b/packages/next/.eslintrc.json similarity index 100% rename from packages/next-auth/.eslintrc.json rename to packages/next/.eslintrc.json diff --git a/packages/next/LICENSE b/packages/next/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/packages/next/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/next/README.md b/packages/next/README.md new file mode 100644 index 000000000..214303860 --- /dev/null +++ b/packages/next/README.md @@ -0,0 +1,5 @@ +# ZenStack Next.js Library + +This package is the runtime library for integrating ZenStack with Next.js. + +Visit [Homepage](https://zenstack.dev) for more details. diff --git a/packages/next/package.json b/packages/next/package.json new file mode 100644 index 000000000..887edb7c2 --- /dev/null +++ b/packages/next/package.json @@ -0,0 +1,37 @@ +{ + "name": "@zenstackhq/next", + "version": "1.0.0-alpha.27", + "displayName": "ZenStack Next.js integration", + "description": "ZenStack Next.js integration", + "homepage": "https://zenstack.dev", + "scripts": { + "clean": "rimraf dist", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ./LICENSE dist", + "watch": "tsc --watch", + "lint": "eslint src --ext ts", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm publish --tag dev" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, + "keywords": [], + "author": "", + "license": "MIT", + "peerDependencies": { + "next": "^12.3.1 || ^13", + "react": "^17.0.2 || ^18" + }, + "dependencies": { + "@zenstackhq/runtime": "workspace:*", + "superjson": "^1.11.0" + }, + "devDependencies": { + "@types/react": "^18.0.26", + "copyfiles": "^2.4.1", + "react": "^17.0.2 || ^18", + "rimraf": "^3.0.2", + "typescript": "^4.9.4" + } +} diff --git a/packages/next/src/index.ts b/packages/next/src/index.ts new file mode 100644 index 000000000..8394dd8a0 --- /dev/null +++ b/packages/next/src/index.ts @@ -0,0 +1 @@ +export * from './request-handler'; diff --git a/packages/next/src/request-handler.ts b/packages/next/src/request-handler.ts new file mode 100644 index 000000000..b49a29284 --- /dev/null +++ b/packages/next/src/request-handler.ts @@ -0,0 +1,168 @@ +import { + DbClientContract, + DbOperations, + isPrismaClientKnownRequestError, + isPrismaClientUnknownRequestError, + isPrismaClientValidationError, +} from '@zenstackhq/runtime'; +import { NextApiRequest, NextApiResponse } from 'next'; +import superjson from 'superjson'; + +/** + * Options for initializing a Next.js API endpoint request handler. + * @see requestHandler + */ +export type RequestHandlerOptions = { + /** + * Callback method for getting a Prisma instance for the given request/response pair. + */ + getPrisma: (req: NextApiRequest, res: NextApiResponse) => Promise | unknown; + + /** + * Whether to use superjson for serialization/deserialization. Defaults to true. + */ + useSuperJson?: boolean; +}; + +/** + * Creates a Next.js API endpoint request handler which encapsulates Prisma CRUD operations. + * + * @param options Options for initialization + * @returns An API endpoint request handler + */ +export function requestHandler( + options: RequestHandlerOptions +): (req: NextApiRequest, res: NextApiResponse) => Promise { + return async (req: NextApiRequest, res: NextApiResponse) => { + let prisma = options.getPrisma(req, res); + if (prisma instanceof Promise) { + prisma = await prisma; + } + if (!prisma) { + res.status(500).send({ + error: 'unable to get prisma from request context', + }); + return; + } + return handleRequest(req, res, prisma as DbClientContract, options); + }; +} + +async function handleRequest( + req: NextApiRequest, + res: NextApiResponse, + prisma: DbClientContract, + options: RequestHandlerOptions +): Promise { + const [model, op] = req.query.path as string[]; + + const dbOp = op as keyof DbOperations; + let args: unknown; + let resCode = 200; + + switch (dbOp) { + case 'create': + case 'createMany': + case 'upsert': + if (req.method !== 'POST') { + res.status(400).send({ error: 'invalid http method' }); + return; + } + args = unmarshal(req.body, options.useSuperJson); + resCode = 201; + break; + + case 'findFirst': + case 'findUnique': + case 'findMany': + case 'aggregate': + case 'groupBy': + case 'count': + if (req.method !== 'GET') { + res.status(400).send({ error: 'invalid http method' }); + return; + } + args = req.query.q ? unmarshal(req.query.q as string, options.useSuperJson) : {}; + break; + + case 'update': + case 'updateMany': + if (req.method !== 'PUT') { + res.status(400).send({ error: 'invalid http method' }); + return; + } + args = unmarshal(req.body, options.useSuperJson); + break; + + case 'delete': + case 'deleteMany': + if (req.method !== 'DELETE') { + res.status(400).send({ error: 'invalid http method' }); + return; + } + args = req.query.q ? unmarshal(req.query.q as string, options.useSuperJson) : {}; + break; + + default: + res.status(400).send({ error: `unknown method name: ${op}` }); + break; + } + + try { + const result = await prisma[model][dbOp](args); + res.status(resCode).send(marshal(result, options.useSuperJson)); + } catch (err) { + if (isPrismaClientKnownRequestError(err)) { + if (err.code === 'P2004') { + // rejected by policy + res.status(403).send({ + prisma: true, + rejectedByPolicy: true, + code: err.code, + message: err.message, + }); + } else { + res.status(400).send({ + prisma: true, + code: err.code, + message: err.message, + }); + } + } else if (isPrismaClientUnknownRequestError(err) || isPrismaClientValidationError(err)) { + res.status(400).send({ + prisma: true, + message: err.message, + }); + } else { + res.status(500).send({ + message: (err as Error).message, + }); + } + } +} + +function marshal(value: unknown, useSuperJson = true) { + return useSuperJson ? JSON.parse(superjson.stringify(value)) : value; +} + +function unmarshal(value: unknown, useSuperJson = true) { + if (!value) { + return value; + } + + if (useSuperJson) { + if (typeof value === 'string') { + return superjson.parse(value); + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = (value as any).json; + if (json && typeof json === 'object') { + return superjson.parse(JSON.stringify(value)); + } else { + return value; + } + } + } else { + return value; + } +} diff --git a/packages/next/tsconfig.json b/packages/next/tsconfig.json new file mode 100644 index 000000000..f6dff1942 --- /dev/null +++ b/packages/next/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "CommonJS", + "lib": ["ESNext", "DOM"], + "sourceMap": true, + "outDir": "dist", + "strict": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "resolveJsonModule": true, + "strictPropertyInitialization": false, + "paths": {} + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/plugins/react/.eslintrc.json b/packages/plugins/react/.eslintrc.json new file mode 100644 index 000000000..0a913e874 --- /dev/null +++ b/packages/plugins/react/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/packages/plugins/react/LICENSE b/packages/plugins/react/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/packages/plugins/react/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/packages/plugins/react/README.md b/packages/plugins/react/README.md new file mode 100644 index 000000000..af12f7f86 --- /dev/null +++ b/packages/plugins/react/README.md @@ -0,0 +1,3 @@ +# ZenStack React plugin & runtime + +This package contains ZenStack plugin and runtime for ReactJS. diff --git a/packages/plugins/react/package.json b/packages/plugins/react/package.json new file mode 100644 index 000000000..cdef9b1ef --- /dev/null +++ b/packages/plugins/react/package.json @@ -0,0 +1,41 @@ +{ + "name": "@zenstackhq/react", + "displayName": "ZenStack plugin and runtime for ReactJS", + "version": "1.0.0-alpha.27", + "description": "ZenStack plugin and runtime for ReactJS", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/zenstackhq/zenstack" + }, + "scripts": { + "clean": "rimraf dist", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ./LICENSE dist", + "watch": "tsc --watch", + "lint": "eslint src --ext ts", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm publish --tag dev" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, + "keywords": [], + "author": "ZenStack Team", + "license": "MIT", + "dependencies": { + "@prisma/generator-helper": "^4.7.1", + "@zenstackhq/sdk": "workspace:*", + "change-case": "^4.1.2", + "decimal.js": "^10.4.2", + "superjson": "^1.11.0", + "swr": "^1.3.0", + "ts-morph": "^16.0.0" + }, + "devDependencies": { + "@types/react": "^18.0.26", + "copyfiles": "^2.4.1", + "rimraf": "^3.0.2", + "typescript": "^4.9.4" + } +} diff --git a/packages/plugins/react/src/index.ts b/packages/plugins/react/src/index.ts new file mode 100644 index 000000000..44b6149c3 --- /dev/null +++ b/packages/plugins/react/src/index.ts @@ -0,0 +1,10 @@ +import { DMMF } from '@prisma/generator-helper'; +import { PluginOptions } from '@zenstackhq/sdk'; +import { Model } from '@zenstackhq/sdk/ast'; +import { generate } from './react-hooks-generator'; + +export const name = 'React'; + +export default async function run(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + return generate(model, options, dmmf); +} diff --git a/packages/plugins/react/src/react-hooks-generator.ts b/packages/plugins/react/src/react-hooks-generator.ts new file mode 100644 index 000000000..d34f97a47 --- /dev/null +++ b/packages/plugins/react/src/react-hooks-generator.ts @@ -0,0 +1,500 @@ +import { DMMF } from '@prisma/generator-helper'; +import { PluginError, PluginOptions } from '@zenstackhq/sdk'; +import { DataModel, isDataModel, Model } from '@zenstackhq/sdk/ast'; +import { camelCase, paramCase } from 'change-case'; +import * as path from 'path'; +import { Project } from 'ts-morph'; + +export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + const project = new Project(); + const models: DataModel[] = []; + const warnings: string[] = []; + + for (const dm of model.declarations.filter((d): d is DataModel => isDataModel(d))) { + const hasAllowRule = dm.attributes.find((attr) => attr.decl.ref?.name === '@@allow'); + if (hasAllowRule) { + models.push(dm); + } + } + + let outDir = options.output as string; + if (!outDir) { + throw new PluginError('"output" option is required'); + } + + if (!path.isAbsolute(outDir)) { + // output dir is resolved relative to the schema file path + outDir = path.join(path.dirname(options.schemaPath), outDir); + } + + generateIndex(project, outDir, models); + + models.forEach((model) => { + const mapping = dmmf.mappings.modelOperations.find((op) => op.model === model.name); + generateModelHooks(project, outDir, model, mapping); + }); + + await project.save(); + return warnings; +} + +function wrapReadbackErrorCheck(code: string) { + return `try { + ${code} + } catch (err: any) { + if (err.prisma && err.code === 'P2004') { + // unable to readback data + return undefined; + } else { + throw err; + } + }`; +} + +function generateModelHooks( + project: Project, + outDir: string, + model: DataModel, + mapping: DMMF.ModelMapping | undefined +) { + const fileName = paramCase(model.name); + const sf = project.createSourceFile(path.join(outDir, `${fileName}.ts`), undefined, { overwrite: true }); + + sf.addStatements('/* eslint-disable */'); + + sf.addImportDeclaration({ + namedImports: ['Prisma', model.name], + isTypeOnly: true, + moduleSpecifier: '@prisma/client', + }); + sf.addStatements([ + `import { useContext } from 'react';`, + `import { RequestHandlerContext, type RequestOptions } from '@zenstackhq/react/runtime';`, + `import * as request from '@zenstackhq/react/runtime';`, + ]); + + const useFunc = sf.addFunction({ + name: `use${model.name}`, + isExported: true, + }); + + const prefixesToMutate = ['find', 'aggregate', 'count', 'groupBy']; + const modelRouteName = camelCase(model.name); + + useFunc.addStatements([ + 'const { endpoint } = useContext(RequestHandlerContext);', + `const prefixesToMutate = [${prefixesToMutate + .map((prefix) => '`${endpoint}/' + modelRouteName + '/' + prefix + '`') + .join(', ')}];`, + 'const mutate = request.getMutate(prefixesToMutate);', + ]); + + const methods: string[] = []; + + // create is somehow named "createOne" in the DMMF + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (mapping?.create || (mapping as any)?.createOne) { + methods.push('create'); + const argsType = `Prisma.${model.name}CreateArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.CheckSelect>`; + useFunc + .addFunction({ + name: 'create', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + wrapReadbackErrorCheck( + `return await request.post<${inputType}, ${returnType}>(\`\${endpoint}/${modelRouteName}/create\`, args, mutate);` + ), + ]); + } + + // createMany + if (mapping?.createMany) { + methods.push('createMany'); + const argsType = `Prisma.${model.name}CreateManyArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.BatchPayload`; + useFunc + .addFunction({ + name: 'createMany', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + `return await request.post<${inputType}, ${returnType}>(\`\${endpoint}/${modelRouteName}/createMany\`, args, mutate);`, + ]); + } + + // findMany + if (mapping?.findMany) { + methods.push('findMany'); + const argsType = `Prisma.${model.name}FindManyArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Array>`; + useFunc + .addFunction({ + name: 'findMany', + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args?', + type: inputType, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/findMany\`, args, options);`, + ]); + } + + // findUnique + if (mapping?.findUnique) { + methods.push('findUnique'); + const argsType = `Prisma.${model.name}FindUniqueArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.${model.name}GetPayload`; + useFunc + .addFunction({ + name: 'findUnique', + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/findUnique\`, args, options);`, + ]); + } + + // findFirst + if (mapping?.findFirst) { + methods.push('findFirst'); + const argsType = `Prisma.${model.name}FindFirstArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.${model.name}GetPayload`; + useFunc + .addFunction({ + name: 'findFirst', + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/findFirst\`, args, options);`, + ]); + } + + // update + // update is somehow named "updateOne" in the DMMF + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (mapping?.update || (mapping as any).updateOne) { + methods.push('update'); + const argsType = `Prisma.${model.name}UpdateArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.${model.name}GetPayload`; + useFunc + .addFunction({ + name: 'update', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + wrapReadbackErrorCheck( + `return await request.put<${inputType}, ${returnType}>(\`\${endpoint}/${modelRouteName}/update\`, args, mutate);` + ), + ]); + } + + // updateMany + if (mapping?.updateMany) { + methods.push('updateMany'); + const argsType = `Prisma.${model.name}UpdateManyArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.BatchPayload`; + useFunc + .addFunction({ + name: 'updateMany', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + `return await request.put<${inputType}, ${returnType}>(\`\${endpoint}/${modelRouteName}/updateMany\`, args, mutate);`, + ]); + } + + // upsert + // upsert is somehow named "upsertOne" in the DMMF + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (mapping?.upsert || (mapping as any).upsertOne) { + methods.push('upsert'); + const argsType = `Prisma.${model.name}UpsertArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.${model.name}GetPayload`; + useFunc + .addFunction({ + name: 'upsert', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + wrapReadbackErrorCheck( + `return await request.put<${inputType}, ${returnType}>(\`\${endpoint}/${modelRouteName}/upsert\`, args, mutate);` + ), + ]); + } + + // del + // delete is somehow named "deleteOne" in the DMMF + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (mapping?.delete || (mapping as any).deleteOne) { + methods.push('del'); + const argsType = `Prisma.${model.name}DeleteArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.${model.name}GetPayload`; + useFunc + .addFunction({ + name: 'del', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args?', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + wrapReadbackErrorCheck( + `return await request.del<${returnType}>(\`\${endpoint}/${modelRouteName}/delete\`, args, mutate);` + ), + ]); + } + + // deleteMany + if (mapping?.deleteMany) { + methods.push('deleteMany'); + const argsType = `Prisma.${model.name}DeleteManyArgs`; + const inputType = `Prisma.SelectSubset`; + const returnType = `Prisma.BatchPayload`; + useFunc + .addFunction({ + name: 'deleteMany', + isAsync: true, + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args?', + type: inputType, + }, + ], + }) + .addBody() + .addStatements([ + `return await request.del<${returnType}>(\`\${endpoint}/${modelRouteName}/deleteMany\`, args, mutate);`, + ]); + } + + // aggregate + if (mapping?.aggregate) { + methods.push('aggregate'); + const argsType = `Prisma.${model.name}AggregateArgs`; + const inputType = `Prisma.Subset`; + const returnType = `Prisma.Get${model.name}AggregateType`; + useFunc + .addFunction({ + name: 'aggregate', + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/aggregate\`, args, options);`, + ]); + } + + // groupBy + if (mapping?.groupBy) { + methods.push('groupBy'); + const returnType = `{} extends InputErrors ? Prisma.Get${model.name}GroupByPayload : InputErrors`; + useFunc + .addFunction({ + name: 'groupBy', + typeParameters: [ + `T extends Prisma.${model.name}GroupByArgs`, + `HasSelectOrTake extends Prisma.Or>, Prisma.Extends<'take', Prisma.Keys>>`, + `OrderByArg extends Prisma.True extends HasSelectOrTake ? { orderBy: Prisma.UserGroupByArgs['orderBy'] }: { orderBy?: Prisma.UserGroupByArgs['orderBy'] },`, + `OrderFields extends Prisma.ExcludeUnderscoreKeys>>`, + `ByFields extends Prisma.TupleToUnion`, + `ByValid extends Prisma.Has`, + `HavingFields extends Prisma.GetHavingFields`, + `HavingValid extends Prisma.Has`, + `ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False`, + `InputErrors extends ByEmpty extends Prisma.True + ? \`Error: "by" must not be empty.\` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? \`Error: Field "\${P}" used in "having" needs to be provided in "by".\` + : [ + Error, + 'Field ', + P, + \` in "having" needs to be provided in "by"\`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields]`, + ], + parameters: [ + { + name: 'args', + type: `Prisma.SubsetIntersection & InputErrors`, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/groupBy\`, args, options);`, + ]); + } + + // count + if (mapping?.count) { + methods.push('count'); + const argsType = `Prisma.${model.name}CountArgs`; + const inputType = `Prisma.Subset`; + const returnType = `T extends { select: any; } ? T['select'] extends true ? number : Prisma.GetScalarType : number`; + useFunc + .addFunction({ + name: 'count', + typeParameters: [`T extends ${argsType}`], + parameters: [ + { + name: 'args', + type: inputType, + }, + { + name: 'options?', + type: `RequestOptions<${returnType}>`, + }, + ], + }) + .addBody() + .addStatements([ + `return request.get<${returnType}>(\`\${endpoint}/${modelRouteName}/count\`, args, options);`, + ]); + } + + useFunc.addStatements([`return { ${methods.join(', ')} };`]); + + sf.formatText(); +} + +function generateIndex(project: Project, outDir: string, models: DataModel[]) { + const sf = project.createSourceFile(path.join(outDir, 'index.ts'), undefined, { overwrite: true }); + + sf.addStatements(models.map((d) => `export * from './${paramCase(d.name)}';`)); + + sf.formatText(); +} diff --git a/packages/runtime/src/request.ts b/packages/plugins/react/src/runtime.ts similarity index 71% rename from packages/runtime/src/request.ts rename to packages/plugins/react/src/runtime.ts index ec9d9c99d..64789236e 100644 --- a/packages/runtime/src/request.ts +++ b/packages/plugins/react/src/runtime.ts @@ -1,12 +1,17 @@ +import { createContext } from 'react'; import superjson from 'superjson'; import useSWR, { useSWRConfig } from 'swr'; -import type { - MutatorCallback, - MutatorOptions, - SWRResponse, -} from 'swr/dist/types'; +import type { MutatorCallback, MutatorOptions, SWRResponse } from 'swr/dist/types'; import { registerSerializers } from './serialization-utils'; -import { RequestOptions } from './types'; + +/** + * Client request options + */ +export type RequestOptions = { + // disable data fetching + disabled?: boolean; + initialData?: T; +}; // register superjson custom serializers registerSerializers(); @@ -70,11 +75,7 @@ export function get( * @param data The request data. * @param mutate Mutator for invalidating cache. */ -export async function post( - url: string, - data: Data, - mutate: Mutator -): Promise { +export async function post(url: string, data: Data, mutate: Mutator): Promise { const r: Result = await fetcher(url, { method: 'POST', headers: { @@ -82,7 +83,7 @@ export async function post( }, body: marshal(data), }); - mutate(url, true); + mutate(); return r; } @@ -93,11 +94,7 @@ export async function post( * @param data The request data. * @param mutate Mutator for invalidating cache. */ -export async function put( - url: string, - data: Data, - mutate: Mutator -): Promise { +export async function put(url: string, data: Data, mutate: Mutator): Promise { const r: Result = await fetcher(url, { method: 'PUT', headers: { @@ -105,7 +102,7 @@ export async function put( }, body: marshal(data), }); - mutate(url, true); + mutate(); return r; } @@ -116,51 +113,53 @@ export async function put( * @param args The request args object, which will be superjson-stringified and appended as "?q=" parameter * @param mutate Mutator for invalidating cache. */ -export async function del( - url: string, - args: unknown, - mutate: Mutator -): Promise { +export async function del(url: string, args: unknown, mutate: Mutator): Promise { const reqUrl = makeUrl(url, args); const r: Result = await fetcher(reqUrl, { method: 'DELETE', }); const path = url.split('/'); path.pop(); - mutate(path.join('/'), true); + mutate(); return r; } type Mutator = ( - key: string, - prefix: boolean, data?: unknown | Promise | MutatorCallback, opts?: boolean | MutatorOptions ) => Promise; -export function getMutate(): Mutator { +export function getMutate(prefixes: string[]): Mutator { // https://swr.vercel.app/docs/advanced/cache#mutate-multiple-keys-from-regex const { cache, mutate } = useSWRConfig(); - return ( - key: string, - prefix: boolean, - data?: unknown | Promise | MutatorCallback, - opts?: boolean | MutatorOptions - ) => { - if (!prefix) { - return mutate(key, data, opts); - } - + return (data?: unknown | Promise | MutatorCallback, opts?: boolean | MutatorOptions) => { if (!(cache instanceof Map)) { - throw new Error( - 'mutate requires the cache provider to be a Map instance' - ); + throw new Error('mutate requires the cache provider to be a Map instance'); } const keys = Array.from(cache.keys()).filter( - (k) => typeof k === 'string' && k.startsWith(key) + (k) => typeof k === 'string' && prefixes.some((prefix) => k.startsWith(prefix)) ) as string[]; const mutations = keys.map((key) => mutate(key, data, opts)); return Promise.all(mutations); }; } + +/** + * Context type for configuring react hooks. + */ +export type RequestHandlerContext = { + endpoint: string; +}; + +/** + * Context for configuring react hooks. + */ +export const RequestHandlerContext = createContext({ + endpoint: '/api/model', +}); + +/** + * Context provider. + */ +export const Provider = RequestHandlerContext.Provider; diff --git a/packages/plugins/react/src/serialization-utils.ts b/packages/plugins/react/src/serialization-utils.ts new file mode 100644 index 000000000..15442900b --- /dev/null +++ b/packages/plugins/react/src/serialization-utils.ts @@ -0,0 +1,21 @@ +import Decimal from 'decimal.js'; +import superjson from 'superjson'; + +export function registerSerializers() { + superjson.registerCustom( + { + isApplicable: (v): v is Buffer => Buffer.isBuffer(v), + serialize: (v) => JSON.stringify(v.toJSON().data), + deserialize: (v) => Buffer.from(JSON.parse(v)), + }, + 'Buffer' + ); + superjson.registerCustom( + { + isApplicable: (v): v is Decimal => Decimal.isDecimal(v), + serialize: (v) => v.toJSON(), + deserialize: (v) => new Decimal(v), + }, + 'decimal.js' + ); +} diff --git a/packages/schema/src/res/tsconfig.template.json b/packages/plugins/react/tsconfig.json similarity index 52% rename from packages/schema/src/res/tsconfig.template.json rename to packages/plugins/react/tsconfig.json index 21606d617..e2c5bd0f3 100644 --- a/packages/schema/src/res/tsconfig.template.json +++ b/packages/plugins/react/tsconfig.json @@ -4,14 +4,18 @@ "module": "CommonJS", "lib": ["ESNext", "DOM"], "sourceMap": true, - "outDir": "lib", + "outDir": "dist", "strict": true, + "noUnusedLocals": true, + "noImplicitReturns": true, "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, "declaration": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "strictPropertyInitialization": false, + "paths": {} }, - "include": ["**/*.ts"], - "exclude": ["node_modules", ".prisma", "**/*.d.ts"] + "include": ["src/**/*.ts"] } diff --git a/packages/plugins/trpc/.eslintrc.json b/packages/plugins/trpc/.eslintrc.json new file mode 100644 index 000000000..0a913e874 --- /dev/null +++ b/packages/plugins/trpc/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/packages/plugins/trpc/LICENSE b/packages/plugins/trpc/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/packages/plugins/trpc/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/packages/plugins/trpc/README.md b/packages/plugins/trpc/README.md new file mode 100644 index 000000000..392982366 --- /dev/null +++ b/packages/plugins/trpc/README.md @@ -0,0 +1,3 @@ +# ZenStack tRPC plugin + +This package contains ZenStack plugin for tRPC. It's based on [prisma-trpc-generator](https://github.com/omar-dulaimi/prisma-trpc-generator). diff --git a/packages/plugins/trpc/package.json b/packages/plugins/trpc/package.json new file mode 100644 index 000000000..bbdd85552 --- /dev/null +++ b/packages/plugins/trpc/package.json @@ -0,0 +1,42 @@ +{ + "name": "@zenstackhq/trpc", + "displayName": "ZenStack plugin for tRPC", + "version": "1.0.0-alpha.27", + "description": "ZenStack plugin for tRPC", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/zenstackhq/zenstack" + }, + "scripts": { + "clean": "rimraf dist", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ./LICENSE dist", + "watch": "tsc --watch", + "lint": "eslint src --ext ts", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm publish --tag dev" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, + "keywords": [], + "author": "ZenStack Team", + "license": "MIT", + "dependencies": { + "@prisma/generator-helper": "^4.7.1", + "@prisma/internals": "^4.7.1", + "@zenstackhq/sdk": "workspace:*", + "change-case": "^4.1.2", + "prettier": "^2.8.3", + "ts-morph": "^16.0.0", + "tslib": "^2.4.1", + "zod": "^3.19.1" + }, + "devDependencies": { + "@types/prettier": "^2.7.2", + "copyfiles": "^2.4.1", + "rimraf": "^3.0.2", + "typescript": "^4.9.4" + } +} diff --git a/packages/plugins/trpc/src/config.ts b/packages/plugins/trpc/src/config.ts new file mode 100644 index 000000000..dfd81439a --- /dev/null +++ b/packages/plugins/trpc/src/config.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +export const configSchema = z.object({ + contextPath: z.string().default('../../../../src/context'), +}); + +export type Config = z.infer; diff --git a/packages/plugins/trpc/src/generator.ts b/packages/plugins/trpc/src/generator.ts new file mode 100644 index 000000000..1ef8873e1 --- /dev/null +++ b/packages/plugins/trpc/src/generator.ts @@ -0,0 +1,164 @@ +import { DMMF } from '@prisma/generator-helper'; +import { PluginError, PluginOptions } from '@zenstackhq/sdk'; +import { Model } from '@zenstackhq/sdk/ast'; +import { promises as fs } from 'fs'; +import path from 'path'; +import { generate as PrismaZodGenerator } from './zod/generator'; +import { generateProcedure, generateRouterSchemaImports, getInputTypeByOpName, resolveModelsComments } from './helpers'; +import { project } from './project'; +import removeDir from './utils/removeDir'; +import { camelCase } from 'change-case'; +import { Project } from 'ts-morph'; + +export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + let outDir = options.output as string; + if (!outDir) { + throw new PluginError('"output" option is required'); + } + + if (!path.isAbsolute(outDir)) { + // output dir is resolved relative to the schema file path + outDir = path.join(path.dirname(options.schemaPath), outDir); + } + + await fs.mkdir(outDir, { recursive: true }); + await removeDir(outDir, true); + + await PrismaZodGenerator(model, options, dmmf); + + const prismaClientDmmf = dmmf; + + const modelOperations = prismaClientDmmf.mappings.modelOperations; + const models = prismaClientDmmf.datamodel.models; + const hiddenModels: string[] = []; + resolveModelsComments(models, hiddenModels); + + const appRouter = project.createSourceFile(path.resolve(outDir, 'routers', `index.ts`), undefined, { + overwrite: true, + }); + + appRouter.addStatements('/* eslint-disable */'); + + appRouter.addImportDeclarations([ + { + namedImports: ['AnyRootConfig'], + moduleSpecifier: '@trpc/server', + }, + { + namedImports: ['PrismaClient'], + moduleSpecifier: '@prisma/client', + }, + { + namedImports: ['createRouterFactory'], + moduleSpecifier: '@trpc/server/dist/core/router', + }, + { + namedImports: ['createBuilder'], + moduleSpecifier: '@trpc/server/dist/core/internals/procedureBuilder', + }, + ]); + + appRouter.addStatements(` + export type BaseConfig = AnyRootConfig; + + export type RouterFactory = ReturnType< + typeof createRouterFactory + >; + + export type ProcBuilder = ReturnType< + typeof createBuilder + >; + + export function db(ctx: any) { + if (!ctx.prisma) { + throw new Error('Missing "prisma" field in trpc context'); + } + return ctx.prisma as PrismaClient; + } + + `); + + const createFunction = appRouter.addFunction({ + name: 'createRouter', + parameters: [ + { name: 'router', type: 'RouterFactory' }, + { name: 'procedure', type: 'ProcBuilder' }, + ], + isExported: true, + }); + + createFunction.setBodyText((writer) => { + writer.write('return router('); + writer.block(() => { + for (const modelOperation of modelOperations) { + const { model, ...operations } = modelOperation; + if (hiddenModels.includes(model)) { + continue; + } + + generateModelCreateRouter(project, model, operations, outDir); + + appRouter.addImportDeclaration({ + defaultImport: `create${model}Router`, + moduleSpecifier: `./${model}.router`, + }); + + writer.writeLine(`${camelCase(model)}: create${model}Router(router, procedure),`); + } + }); + writer.write(');'); + }); + + appRouter.formatText(); + await project.save(); +} + +function generateModelCreateRouter( + project: Project, + model: string, + operations: Record, + outputDir: string +) { + const modelRouter = project.createSourceFile(path.resolve(outputDir, 'routers', `${model}.router.ts`), undefined, { + overwrite: true, + }); + + modelRouter.addStatements('/* eslint-disable */'); + + modelRouter.addImportDeclarations([ + { + namedImports: ['type RouterFactory', 'type ProcBuilder', 'type BaseConfig', 'db'], + moduleSpecifier: '.', + }, + ]); + + generateRouterSchemaImports(modelRouter, model); + + modelRouter + .addFunction({ + name: 'createRouter', + parameters: [ + { name: 'router', type: 'RouterFactory' }, + { name: 'procedure', type: 'ProcBuilder' }, + ], + isExported: true, + isDefaultExport: true, + }) + .setBodyText((writer) => { + writer.write('return router('); + writer.block(() => { + for (const [opType, opNameWithModel] of Object.entries(operations)) { + const baseOpType = opType.replace('OrThrow', ''); + + const inputType = getInputTypeByOpName(baseOpType, model); + + if (opNameWithModel && inputType) { + generateProcedure(writer, opType.replace(/One$/, ''), inputType, model, baseOpType); + } + } + }); + writer.write(');'); + }); + + modelRouter.formatText(); +} diff --git a/packages/plugins/trpc/src/helpers.ts b/packages/plugins/trpc/src/helpers.ts new file mode 100644 index 000000000..8eb4e75d8 --- /dev/null +++ b/packages/plugins/trpc/src/helpers.ts @@ -0,0 +1,148 @@ +import { DMMF } from '@prisma/generator-helper'; +import { CodeBlockWriter, SourceFile } from 'ts-morph'; +import { uncapitalizeFirstLetter } from './utils/uncapitalizeFirstLetter'; + +export const generatetRPCImport = (sourceFile: SourceFile) => { + sourceFile.addImportDeclaration({ + moduleSpecifier: '@trpc/server', + namespaceImport: 'trpc', + }); +}; + +export const generateRouterImport = (sourceFile: SourceFile, modelNamePlural: string, modelNameCamelCase: string) => { + sourceFile.addImportDeclaration({ + moduleSpecifier: `./${modelNameCamelCase}.router`, + namedImports: [`${modelNamePlural}Router`], + }); +}; + +export function generateProcedure( + writer: CodeBlockWriter, + opType: string, + typeName: string, + modelName: string, + baseOpType: string +) { + const procType = getProcedureTypeByOpName(baseOpType); + writer.write(` + ${opType}: procedure.input(${typeName}).${procType}(({ctx, input}) => db(ctx).${uncapitalizeFirstLetter( + modelName + )}.${opType.replace('One', '')}(input)), + `); +} + +export function generateRouterSchemaImports(sourceFile: SourceFile, name: string) { + sourceFile.addStatements(`import { ${name}Schema } from '../schemas/${name}.schema';`); +} + +export const getInputTypeByOpName = (opName: string, modelName: string) => { + let inputType; + switch (opName) { + case 'findUnique': + inputType = `${modelName}Schema.findUnique`; + break; + case 'findFirst': + inputType = `${modelName}Schema.findFirst`; + break; + case 'findMany': + inputType = `${modelName}Schema.findMany`; + break; + case 'findRaw': + inputType = `${modelName}Schema.findRawObject`; + break; + case 'createOne': + inputType = `${modelName}Schema.create`; + break; + case 'createMany': + inputType = `${modelName}Schema.createMany`; + break; + case 'deleteOne': + inputType = `${modelName}Schema.delete`; + break; + case 'updateOne': + inputType = `${modelName}Schema.update`; + break; + case 'deleteMany': + inputType = `${modelName}Schema.deleteMany`; + break; + case 'updateMany': + inputType = `${modelName}Schema.updateMany`; + break; + case 'upsertOne': + inputType = `${modelName}Schema.upsert`; + break; + case 'aggregate': + inputType = `${modelName}Schema.aggregate`; + break; + case 'aggregateRaw': + inputType = `${modelName}Schema.aggregateRawObject`; + break; + case 'groupBy': + inputType = `${modelName}Schema.groupBy`; + break; + default: + console.log('getInputTypeByOpName: ', { opName, modelName }); + } + return inputType; +}; + +export const getProcedureTypeByOpName = (opName: string) => { + let procType; + switch (opName) { + case 'findUnique': + case 'findFirst': + case 'findMany': + case 'findRaw': + case 'aggregate': + case 'aggregateRaw': + case 'groupBy': + procType = 'query'; + break; + case 'createOne': + case 'createMany': + case 'deleteOne': + case 'updateOne': + case 'deleteMany': + case 'updateMany': + case 'upsertOne': + procType = 'mutation'; + break; + default: + console.log('getProcedureTypeByOpName: ', { opName }); + } + return procType; +}; + +export function resolveModelsComments(models: DMMF.Model[], hiddenModels: string[]) { + const modelAttributeRegex = /(@@Gen\.)+([A-z])+(\()+(.+)+(\))+/; + const attributeNameRegex = /(?:\.)+([A-Za-z])+(?:\()+/; + const attributeArgsRegex = /(?:\()+([A-Za-z])+:+(.+)+(?:\))+/; + + for (const model of models) { + if (model.documentation) { + const attribute = model.documentation?.match(modelAttributeRegex)?.[0]; + const attributeName = attribute?.match(attributeNameRegex)?.[0]?.slice(1, -1); + if (attributeName !== 'model') continue; + const rawAttributeArgs = attribute?.match(attributeArgsRegex)?.[0]?.slice(1, -1); + + const parsedAttributeArgs: Record = {}; + if (rawAttributeArgs) { + const rawAttributeArgsParts = rawAttributeArgs + .split(':') + .map((it) => it.trim()) + .map((part) => (part.startsWith('[') ? part : part.split(','))) + .flat() + .map((it) => it.trim()); + + for (let i = 0; i < rawAttributeArgsParts.length; i += 2) { + const key = rawAttributeArgsParts[i]; + const value = rawAttributeArgsParts[i + 1]; + parsedAttributeArgs[key] = JSON.parse(value); + } + } + if (parsedAttributeArgs.hide) { + hiddenModels.push(model.name); + } + } + } +} diff --git a/packages/plugins/trpc/src/index.ts b/packages/plugins/trpc/src/index.ts new file mode 100644 index 000000000..578885874 --- /dev/null +++ b/packages/plugins/trpc/src/index.ts @@ -0,0 +1,10 @@ +import { DMMF } from '@prisma/generator-helper'; +import { PluginOptions } from '@zenstackhq/sdk'; +import { Model } from '@zenstackhq/sdk/ast'; +import { generate } from './generator'; + +export const name = 'tRPC'; + +export default async function run(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + return generate(model, options, dmmf); +} diff --git a/packages/plugins/trpc/src/project.ts b/packages/plugins/trpc/src/project.ts new file mode 100644 index 000000000..0a87ba912 --- /dev/null +++ b/packages/plugins/trpc/src/project.ts @@ -0,0 +1,15 @@ +import { Project, ScriptTarget, ModuleKind, CompilerOptions } from 'ts-morph'; + +const compilerOptions: CompilerOptions = { + target: ScriptTarget.ES2019, + module: ModuleKind.CommonJS, + emitDecoratorMetadata: true, + experimentalDecorators: true, + esModuleInterop: true, +}; + +export const project = new Project({ + compilerOptions: { + ...compilerOptions, + }, +}); diff --git a/doc-serve/pages/.gitkeep b/packages/plugins/trpc/src/types.ts similarity index 100% rename from doc-serve/pages/.gitkeep rename to packages/plugins/trpc/src/types.ts diff --git a/packages/plugins/trpc/src/utils/removeDir.ts b/packages/plugins/trpc/src/utils/removeDir.ts new file mode 100644 index 000000000..03f8d74f5 --- /dev/null +++ b/packages/plugins/trpc/src/utils/removeDir.ts @@ -0,0 +1,15 @@ +import path from 'path'; +import { promises as fs } from 'fs'; + +export default async function removeDir(dirPath: string, onlyContent: boolean) { + const dirEntries = await fs.readdir(dirPath, { withFileTypes: true }); + await Promise.all( + dirEntries.map(async (dirEntry) => { + const fullPath = path.join(dirPath, dirEntry.name); + return dirEntry.isDirectory() ? await removeDir(fullPath, false) : await fs.unlink(fullPath); + }) + ); + if (!onlyContent) { + await fs.rmdir(dirPath); + } +} diff --git a/packages/plugins/trpc/src/utils/uncapitalizeFirstLetter.ts b/packages/plugins/trpc/src/utils/uncapitalizeFirstLetter.ts new file mode 100644 index 000000000..827643e89 --- /dev/null +++ b/packages/plugins/trpc/src/utils/uncapitalizeFirstLetter.ts @@ -0,0 +1,3 @@ +export const uncapitalizeFirstLetter = (str: string) => { + return str.charAt(0).toLowerCase() + str.slice(1); +}; diff --git a/packages/plugins/trpc/src/zod/README.md b/packages/plugins/trpc/src/zod/README.md new file mode 100644 index 000000000..6af0cf15b --- /dev/null +++ b/packages/plugins/trpc/src/zod/README.md @@ -0,0 +1 @@ +This plugin is based on [prisma-zod-generator](https://github.com/omar-dulaimi/prisma-zod-generator). diff --git a/packages/plugins/trpc/src/zod/generator.ts b/packages/plugins/trpc/src/zod/generator.ts new file mode 100644 index 000000000..16c8d3fdc --- /dev/null +++ b/packages/plugins/trpc/src/zod/generator.ts @@ -0,0 +1,101 @@ +import { ConnectorType, DMMF } from '@prisma/generator-helper'; +import { Dictionary } from '@prisma/internals'; +import { PluginOptions, getLiteral } from '@zenstackhq/sdk'; +import { DataSource, Model, isDataSource } from '@zenstackhq/sdk/ast'; +import { promises as fs } from 'fs'; +import { + addMissingInputObjectTypes, + hideInputObjectTypesAndRelatedFields, + resolveAddMissingInputObjectTypeOptions, + resolveModelsComments, +} from './helpers'; +import { resolveAggregateOperationSupport } from './helpers/aggregate-helpers'; +import Transformer from './transformer'; +import { AggregateOperationSupport } from './types'; +import removeDir from './utils/removeDir'; + +export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + await handleGeneratorOutputValue((options.output as string) ?? './generated'); + + const prismaClientDmmf = dmmf; + + const modelOperations = prismaClientDmmf.mappings.modelOperations; + const inputObjectTypes = prismaClientDmmf.schema.inputObjectTypes.prisma; + const outputObjectTypes = prismaClientDmmf.schema.outputObjectTypes.prisma; + const enumTypes = prismaClientDmmf.schema.enumTypes; + const models: DMMF.Model[] = prismaClientDmmf.datamodel.models; + const hiddenModels: string[] = []; + const hiddenFields: string[] = []; + resolveModelsComments(models, modelOperations, enumTypes, hiddenModels, hiddenFields); + + await generateEnumSchemas(prismaClientDmmf.schema.enumTypes.prisma, prismaClientDmmf.schema.enumTypes.model ?? []); + + const dataSource = model.declarations.find((d): d is DataSource => isDataSource(d)); + + const dataSourceProvider = getLiteral( + dataSource?.fields.find((f) => f.name === 'provider')?.value + ) as ConnectorType; + + Transformer.provider = dataSourceProvider; + + const generatorConfigOptions: Dictionary = {}; + Object.entries(options).forEach(([k, v]) => (generatorConfigOptions[k] = v as string)); + + const addMissingInputObjectTypeOptions = resolveAddMissingInputObjectTypeOptions(generatorConfigOptions); + addMissingInputObjectTypes( + inputObjectTypes, + outputObjectTypes, + models, + modelOperations, + dataSourceProvider, + addMissingInputObjectTypeOptions + ); + + const aggregateOperationSupport = resolveAggregateOperationSupport(inputObjectTypes); + + hideInputObjectTypesAndRelatedFields(inputObjectTypes, hiddenModels, hiddenFields); + + await generateObjectSchemas(inputObjectTypes); + await generateModelSchemas(models, modelOperations, aggregateOperationSupport); +} + +async function handleGeneratorOutputValue(output: string) { + // create the output directory and delete contents that might exist from a previous run + await fs.mkdir(output, { recursive: true }); + const isRemoveContentsOnly = true; + await removeDir(output, isRemoveContentsOnly); + + Transformer.setOutputPath(output); +} + +async function generateEnumSchemas(prismaSchemaEnum: DMMF.SchemaEnum[], modelSchemaEnum: DMMF.SchemaEnum[]) { + const enumTypes = [...prismaSchemaEnum, ...modelSchemaEnum]; + const enumNames = enumTypes.map((enumItem) => enumItem.name); + Transformer.enumNames = enumNames ?? []; + const transformer = new Transformer({ + enumTypes, + }); + await transformer.generateEnumSchemas(); +} + +async function generateObjectSchemas(inputObjectTypes: DMMF.InputType[]) { + for (let i = 0; i < inputObjectTypes.length; i += 1) { + const fields = inputObjectTypes[i]?.fields; + const name = inputObjectTypes[i]?.name; + const transformer = new Transformer({ name, fields }); + await transformer.generateObjectSchema(); + } +} + +async function generateModelSchemas( + models: DMMF.Model[], + modelOperations: DMMF.ModelMapping[], + aggregateOperationSupport: AggregateOperationSupport +) { + const transformer = new Transformer({ + models, + modelOperations, + aggregateOperationSupport, + }); + await transformer.generateModelSchemas(); +} diff --git a/packages/plugins/trpc/src/zod/helpers/aggregate-helpers.ts b/packages/plugins/trpc/src/zod/helpers/aggregate-helpers.ts new file mode 100644 index 000000000..23ebd8ddf --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/aggregate-helpers.ts @@ -0,0 +1,78 @@ +import { DMMF } from '@prisma/generator-helper'; +import { AggregateOperationSupport } from '../types'; + +const isAggregateOutputType = (name: string) => /(?:Count|Avg|Sum|Min|Max)AggregateOutputType$/.test(name); + +export const isAggregateInputType = (name: string) => + name.endsWith('CountAggregateInput') || + name.endsWith('SumAggregateInput') || + name.endsWith('AvgAggregateInput') || + name.endsWith('MinAggregateInput') || + name.endsWith('MaxAggregateInput'); + +export function addMissingInputObjectTypesForAggregate( + inputObjectTypes: DMMF.InputType[], + outputObjectTypes: DMMF.OutputType[] +) { + const aggregateOutputTypes = outputObjectTypes.filter(({ name }) => isAggregateOutputType(name)); + for (const aggregateOutputType of aggregateOutputTypes) { + const name = aggregateOutputType.name.replace(/(?:OutputType|Output)$/, ''); + inputObjectTypes.push({ + constraints: { maxNumFields: null, minNumFields: null }, + name: `${name}Input`, + fields: aggregateOutputType.fields.map((field) => ({ + name: field.name, + isNullable: false, + isRequired: false, + inputTypes: [ + { + isList: false, + type: 'True', + location: 'scalar', + }, + ], + })), + }); + } +} + +export function resolveAggregateOperationSupport(inputObjectTypes: DMMF.InputType[]) { + const aggregateOperationSupport: AggregateOperationSupport = {}; + for (const inputType of inputObjectTypes) { + if (isAggregateInputType(inputType.name)) { + const name = inputType.name.replace('AggregateInput', ''); + if (name.endsWith('Count')) { + const model = name.replace('Count', ''); + aggregateOperationSupport[model] = { + ...aggregateOperationSupport[model], + count: true, + }; + } else if (name.endsWith('Min')) { + const model = name.replace('Min', ''); + aggregateOperationSupport[model] = { + ...aggregateOperationSupport[model], + min: true, + }; + } else if (name.endsWith('Max')) { + const model = name.replace('Max', ''); + aggregateOperationSupport[model] = { + ...aggregateOperationSupport[model], + max: true, + }; + } else if (name.endsWith('Sum')) { + const model = name.replace('Sum', ''); + aggregateOperationSupport[model] = { + ...aggregateOperationSupport[model], + sum: true, + }; + } else if (name.endsWith('Avg')) { + const model = name.replace('Avg', ''); + aggregateOperationSupport[model] = { + ...aggregateOperationSupport[model], + avg: true, + }; + } + } + } + return aggregateOperationSupport; +} diff --git a/packages/plugins/trpc/src/zod/helpers/comments-helpers.ts b/packages/plugins/trpc/src/zod/helpers/comments-helpers.ts new file mode 100644 index 000000000..13526f53b --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/comments-helpers.ts @@ -0,0 +1,112 @@ +import { DMMF } from '@prisma/generator-helper'; + +const modelAttributeRegex = /(@@Gen\.)+([A-z])+(\()+(.+)+(\))+/; +const attributeNameRegex = /(?:\.)+([A-Za-z])+(?:\()+/; +const attributeArgsRegex = /(?:\()+([A-Za-z])+:+(.+)+(?:\))+/; + +export function resolveModelsComments( + models: DMMF.Model[], + modelOperations: DMMF.ModelMapping[], + enumTypes: { model?: DMMF.SchemaEnum[]; prisma: DMMF.SchemaEnum[] }, + hiddenModels: string[], + hiddenFields: string[] +) { + models = collectHiddenModels(models, hiddenModels); + collectHiddenFields(models, hiddenModels, hiddenFields); + hideModelOperations(models, modelOperations); + hideEnums(enumTypes, hiddenModels); +} + +function collectHiddenModels(models: DMMF.Model[], hiddenModels: string[]) { + return models + .map((model) => { + if (model.documentation) { + const attribute = model.documentation?.match(modelAttributeRegex)?.[0]; + const attributeName = attribute?.match(attributeNameRegex)?.[0]?.slice(1, -1); + if (attributeName !== 'model') model; + const rawAttributeArgs = attribute?.match(attributeArgsRegex)?.[0]?.slice(1, -1); + + const parsedAttributeArgs: Record = {}; + if (rawAttributeArgs) { + const rawAttributeArgsParts = rawAttributeArgs + .split(':') + .map((it) => it.trim()) + .map((part) => (part.startsWith('[') ? part : part.split(','))) + .flat() + .map((it) => it.trim()); + + for (let i = 0; i < rawAttributeArgsParts.length; i += 2) { + const key = rawAttributeArgsParts[i]; + const value = rawAttributeArgsParts[i + 1]; + parsedAttributeArgs[key] = JSON.parse(value); + } + } + if (parsedAttributeArgs.hide) { + hiddenModels.push(model.name); + return null as unknown as DMMF.Model; + } + } + return model; + }) + .filter(Boolean); +} + +function collectHiddenFields(models: DMMF.Model[], hiddenModels: string[], hiddenFields: string[]) { + models.forEach((model) => { + model.fields.forEach((field) => { + if (hiddenModels.includes(field.type)) { + hiddenFields.push(field.name); + if (field.relationFromFields) { + field.relationFromFields.forEach((item) => hiddenFields.push(item)); + } + } + }); + }); +} +function hideEnums(enumTypes: { model?: DMMF.SchemaEnum[]; prisma: DMMF.SchemaEnum[] }, hiddenModels: string[]) { + enumTypes.prisma = enumTypes.prisma.filter((item) => !hiddenModels.find((model) => item.name.startsWith(model))); +} + +function hideModelOperations(models: DMMF.Model[], modelOperations: DMMF.ModelMapping[]) { + let i = modelOperations.length; + while (i >= 0) { + --i; + const modelOperation = modelOperations[i]; + if ( + modelOperation && + !models.find((model) => { + return model.name === modelOperation.model; + }) + ) { + modelOperations.splice(i, 1); + } + } +} + +export function hideInputObjectTypesAndRelatedFields( + inputObjectTypes: DMMF.InputType[], + hiddenModels: string[], + hiddenFields: string[] +) { + let j = inputObjectTypes.length; + while (j >= 0) { + --j; + const inputType = inputObjectTypes[j]; + if ( + inputType && + (hiddenModels.includes(inputType?.meta?.source as string) || + hiddenModels.find((model) => inputType.name.startsWith(model))) + ) { + inputObjectTypes.splice(j, 1); + } else { + let k = inputType?.fields?.length ?? 0; + while (k >= 0) { + --k; + const field = inputType?.fields?.[k]; + if (field && hiddenFields.includes(field.name)) { + inputObjectTypes[j].fields.splice(k, 1); + } + } + } + } +} diff --git a/packages/plugins/trpc/src/zod/helpers/helpers.ts b/packages/plugins/trpc/src/zod/helpers/helpers.ts new file mode 100644 index 000000000..92224754d --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/helpers.ts @@ -0,0 +1,52 @@ +import { DMMF, ConnectorType, Dictionary } from '@prisma/generator-helper'; +import Transformer from '../transformer'; +import { addMissingInputObjectTypesForMongoDbRawOpsAndQueries } from './mongodb-helpers'; +import { addMissingInputObjectTypesForAggregate } from './aggregate-helpers'; +import { addMissingInputObjectTypesForSelect } from './select-helpers'; +import { addMissingInputObjectTypesForInclude } from './include-helpers'; +import { addMissingInputObjectTypesForModelArgs } from './modelArgs-helpers'; + +interface AddMissingInputObjectTypeOptions { + isGenerateSelect: boolean; + isGenerateInclude: boolean; +} + +export function addMissingInputObjectTypes( + inputObjectTypes: DMMF.InputType[], + outputObjectTypes: DMMF.OutputType[], + models: DMMF.Model[], + modelOperations: DMMF.ModelMapping[], + dataSourceProvider: ConnectorType, + options: AddMissingInputObjectTypeOptions +) { + // TODO: remove once Prisma fix this issue: https://github.com/prisma/prisma/issues/14900 + if (dataSourceProvider === 'mongodb') { + addMissingInputObjectTypesForMongoDbRawOpsAndQueries(modelOperations, outputObjectTypes, inputObjectTypes); + } + addMissingInputObjectTypesForAggregate(inputObjectTypes, outputObjectTypes); + if (options.isGenerateSelect) { + addMissingInputObjectTypesForSelect(inputObjectTypes, outputObjectTypes, models); + Transformer.setIsGenerateSelect(true); + } + if (options.isGenerateSelect || options.isGenerateInclude) { + addMissingInputObjectTypesForModelArgs( + inputObjectTypes, + models, + options.isGenerateSelect, + options.isGenerateInclude + ); + } + if (options.isGenerateInclude) { + addMissingInputObjectTypesForInclude(inputObjectTypes, models, options.isGenerateSelect); + Transformer.setIsGenerateInclude(true); + } +} + +export function resolveAddMissingInputObjectTypeOptions( + generatorConfigOptions: Dictionary +): AddMissingInputObjectTypeOptions { + return { + isGenerateSelect: generatorConfigOptions.isGenerateSelect !== 'false', + isGenerateInclude: generatorConfigOptions.isGenerateInclude !== 'false', + }; +} diff --git a/packages/plugins/trpc/src/zod/helpers/include-helpers.ts b/packages/plugins/trpc/src/zod/helpers/include-helpers.ts new file mode 100644 index 000000000..686b678a7 --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/include-helpers.ts @@ -0,0 +1,88 @@ +import { DMMF } from '@prisma/generator-helper'; +import { checkIsModelRelationField, checkModelHasModelRelation, checkModelHasManyModelRelation } from './model-helpers'; + +export function addMissingInputObjectTypesForInclude( + inputObjectTypes: DMMF.InputType[], + models: DMMF.Model[], + isGenerateSelect: boolean +) { + // generate input object types necessary to support ModelInclude with relation support + const generatedIncludeInputObjectTypes = generateModelIncludeInputObjectTypes(models, isGenerateSelect); + + for (const includeInputObjectType of generatedIncludeInputObjectTypes) { + inputObjectTypes.push(includeInputObjectType); + } +} +function generateModelIncludeInputObjectTypes(models: DMMF.Model[], isGenerateSelect: boolean) { + const modelIncludeInputObjectTypes: DMMF.InputType[] = []; + for (const model of models) { + const { name: modelName, fields: modelFields } = model; + const fields: DMMF.SchemaArg[] = []; + + for (const modelField of modelFields) { + const { name: modelFieldName, isList, type } = modelField; + + const isRelationField = checkIsModelRelationField(modelField); + + if (isRelationField) { + const field: DMMF.SchemaArg = { + name: modelFieldName, + isRequired: false, + isNullable: false, + inputTypes: [ + { isList: false, type: 'Boolean', location: 'scalar' }, + { + isList: false, + type: isList ? `${type}FindManyArgs` : `${type}Args`, + location: 'inputObjectTypes', + namespace: 'prisma', + }, + ], + }; + fields.push(field); + } + } + + /** + * include is not generated for models that do not have a relation with any other models + * -> continue onto the next model + */ + const hasRelationToAnotherModel = checkModelHasModelRelation(model); + if (!hasRelationToAnotherModel) { + continue; + } + + const hasManyRelationToAnotherModel = checkModelHasManyModelRelation(model); + + const shouldAddCountField = hasManyRelationToAnotherModel; + if (shouldAddCountField) { + const inputTypes: DMMF.SchemaArgInputType[] = [{ isList: false, type: 'Boolean', location: 'scalar' }]; + if (isGenerateSelect) { + inputTypes.push({ + isList: false, + type: `${modelName}CountOutputTypeArgs`, + location: 'inputObjectTypes', + namespace: 'prisma', + }); + } + const _countField: DMMF.SchemaArg = { + name: '_count', + isRequired: false, + isNullable: false, + inputTypes, + }; + fields.push(_countField); + } + + const modelIncludeInputObjectType: DMMF.InputType = { + name: `${modelName}Include`, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields, + }; + modelIncludeInputObjectTypes.push(modelIncludeInputObjectType); + } + return modelIncludeInputObjectTypes; +} diff --git a/packages/plugins/trpc/src/zod/helpers/index.ts b/packages/plugins/trpc/src/zod/helpers/index.ts new file mode 100644 index 000000000..64ce0cf0c --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/index.ts @@ -0,0 +1,4 @@ +export * from './comments-helpers'; +export * from './helpers'; +export * from './model-helpers'; +export * from './mongodb-helpers'; diff --git a/packages/plugins/trpc/src/zod/helpers/model-helpers.ts b/packages/plugins/trpc/src/zod/helpers/model-helpers.ts new file mode 100644 index 000000000..255638bd8 --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/model-helpers.ts @@ -0,0 +1,36 @@ +import { DMMF } from '@prisma/generator-helper'; + +export function checkModelHasModelRelation(model: DMMF.Model) { + const { fields: modelFields } = model; + for (const modelField of modelFields) { + const isRelationField = checkIsModelRelationField(modelField); + if (isRelationField) { + return true; + } + } + return false; +} + +export function checkModelHasManyModelRelation(model: DMMF.Model) { + const { fields: modelFields } = model; + for (const modelField of modelFields) { + const isManyRelationField = checkIsManyModelRelationField(modelField); + if (isManyRelationField) { + return true; + } + } + return false; +} + +export function checkIsModelRelationField(modelField: DMMF.Field) { + const { kind, relationName } = modelField; + return kind === 'object' && !!relationName; +} + +export function checkIsManyModelRelationField(modelField: DMMF.Field) { + return checkIsModelRelationField(modelField) && modelField.isList; +} + +export function findModelByName(models: DMMF.Model[], modelName: string) { + return models.find(({ name }) => name === modelName); +} diff --git a/packages/plugins/trpc/src/zod/helpers/modelArgs-helpers.ts b/packages/plugins/trpc/src/zod/helpers/modelArgs-helpers.ts new file mode 100644 index 000000000..e03759fe6 --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/modelArgs-helpers.ts @@ -0,0 +1,73 @@ +import { DMMF } from '@prisma/generator-helper'; +import { checkModelHasModelRelation } from './model-helpers'; + +export function addMissingInputObjectTypesForModelArgs( + inputObjectTypes: DMMF.InputType[], + models: DMMF.Model[], + isGenerateSelect: boolean, + isGenerateInclude: boolean +) { + const modelArgsInputObjectTypes = generateModelArgsInputObjectTypes(models, isGenerateSelect, isGenerateInclude); + + for (const modelArgsInputObjectType of modelArgsInputObjectTypes) { + inputObjectTypes.push(modelArgsInputObjectType); + } +} +function generateModelArgsInputObjectTypes( + models: DMMF.Model[], + isGenerateSelect: boolean, + isGenerateInclude: boolean +) { + const modelArgsInputObjectTypes: DMMF.InputType[] = []; + for (const model of models) { + const { name: modelName } = model; + const fields: DMMF.SchemaArg[] = []; + + if (isGenerateSelect) { + const selectField: DMMF.SchemaArg = { + name: 'select', + isRequired: false, + isNullable: false, + inputTypes: [ + { + isList: false, + type: `${modelName}Select`, + location: 'inputObjectTypes', + namespace: 'prisma', + }, + ], + }; + fields.push(selectField); + } + + const hasRelationToAnotherModel = checkModelHasModelRelation(model); + + if (isGenerateInclude && hasRelationToAnotherModel) { + const includeField: DMMF.SchemaArg = { + name: 'include', + isRequired: false, + isNullable: false, + inputTypes: [ + { + isList: false, + type: `${modelName}Include`, + location: 'inputObjectTypes', + namespace: 'prisma', + }, + ], + }; + fields.push(includeField); + } + + const modelArgsInputObjectType: DMMF.InputType = { + name: `${modelName}Args`, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields, + }; + modelArgsInputObjectTypes.push(modelArgsInputObjectType); + } + return modelArgsInputObjectTypes; +} diff --git a/packages/plugins/trpc/src/zod/helpers/mongodb-helpers.ts b/packages/plugins/trpc/src/zod/helpers/mongodb-helpers.ts new file mode 100644 index 000000000..9377925b4 --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/mongodb-helpers.ts @@ -0,0 +1,73 @@ +import { DMMF } from '@prisma/generator-helper'; +import Transformer from '../transformer'; + +export function addMissingInputObjectTypesForMongoDbRawOpsAndQueries( + modelOperations: DMMF.ModelMapping[], + outputObjectTypes: DMMF.OutputType[], + inputObjectTypes: DMMF.InputType[] +) { + const rawOpsMap = resolveMongoDbRawOperations(modelOperations); + Transformer.rawOpsMap = rawOpsMap ?? {}; + + const mongoDbRawQueryInputObjectTypes = resolveMongoDbRawQueryInputObjectTypes(outputObjectTypes); + for (const mongoDbRawQueryInputType of mongoDbRawQueryInputObjectTypes) { + inputObjectTypes.push(mongoDbRawQueryInputType); + } +} + +function resolveMongoDbRawOperations(modelOperations: DMMF.ModelMapping[]) { + const rawOpsMap: { [name: string]: string } = {}; + const rawOpsNames = [ + ...new Set( + modelOperations.reduce((result, current) => { + const keys = Object.keys(current); + keys?.forEach((key) => { + if (key.includes('Raw')) { + result.push(key); + } + }); + return result; + }, []) + ), + ]; + + const modelNames = modelOperations.map((item) => item.model); + + rawOpsNames.forEach((opName) => { + modelNames.forEach((modelName) => { + const isFind = opName === 'findRaw'; + const opWithModel = `${opName.replace('Raw', '')}${modelName}Raw`; + rawOpsMap[opWithModel] = isFind ? `${modelName}FindRawArgs` : `${modelName}AggregateRawArgs`; + }); + }); + + return rawOpsMap; +} + +function resolveMongoDbRawQueryInputObjectTypes(outputObjectTypes: DMMF.OutputType[]) { + const mongoDbRawQueries = getMongoDbRawQueries(outputObjectTypes); + const mongoDbRawQueryInputObjectTypes = mongoDbRawQueries.map((item) => ({ + name: item.name, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields: item.args.map((arg) => ({ + name: arg.name, + isRequired: arg.isRequired, + isNullable: arg.isNullable, + inputTypes: arg.inputTypes, + })), + })); + return mongoDbRawQueryInputObjectTypes; +} + +function getMongoDbRawQueries(outputObjectTypes: DMMF.OutputType[]) { + const queryOutputTypes = outputObjectTypes.filter((item) => item.name === 'Query'); + + const mongodbRawQueries = queryOutputTypes?.[0].fields.filter((field) => field.name.includes('Raw')) ?? []; + + return mongodbRawQueries; +} + +export const isMongodbRawOp = (name: string) => /find([^]*?)Raw/.test(name) || /aggregate([^]*?)Raw/.test(name); diff --git a/packages/plugins/trpc/src/zod/helpers/select-helpers.ts b/packages/plugins/trpc/src/zod/helpers/select-helpers.ts new file mode 100644 index 000000000..691bf0269 --- /dev/null +++ b/packages/plugins/trpc/src/zod/helpers/select-helpers.ts @@ -0,0 +1,155 @@ +import { DMMF } from '@prisma/generator-helper'; +import { checkIsModelRelationField, checkModelHasManyModelRelation } from './model-helpers'; + +export function addMissingInputObjectTypesForSelect( + inputObjectTypes: DMMF.InputType[], + outputObjectTypes: DMMF.OutputType[], + models: DMMF.Model[] +) { + // generate input object types necessary to support ModelSelect._count + const modelCountOutputTypes = getModelCountOutputTypes(outputObjectTypes); + const modelCountOutputTypeSelectInputObjectTypes = + generateModelCountOutputTypeSelectInputObjectTypes(modelCountOutputTypes); + const modelCountOutputTypeArgsInputObjectTypes = + generateModelCountOutputTypeArgsInputObjectTypes(modelCountOutputTypes); + + const modelSelectInputObjectTypes = generateModelSelectInputObjectTypes(models); + + const generatedInputObjectTypes = [ + modelCountOutputTypeSelectInputObjectTypes, + modelCountOutputTypeArgsInputObjectTypes, + modelSelectInputObjectTypes, + ].flat(); + + for (const inputObjectType of generatedInputObjectTypes) { + inputObjectTypes.push(inputObjectType); + } +} + +function getModelCountOutputTypes(outputObjectTypes: DMMF.OutputType[]) { + return outputObjectTypes.filter(({ name }) => name.includes('CountOutputType')); +} + +function generateModelCountOutputTypeSelectInputObjectTypes(modelCountOutputTypes: DMMF.OutputType[]) { + const modelCountOutputTypeSelectInputObjectTypes: DMMF.InputType[] = []; + for (const modelCountOutputType of modelCountOutputTypes) { + const { name: modelCountOutputTypeName, fields: modelCountOutputTypeFields } = modelCountOutputType; + const modelCountOutputTypeSelectInputObjectType: DMMF.InputType = { + name: `${modelCountOutputTypeName}Select`, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields: modelCountOutputTypeFields.map(({ name }) => ({ + name, + isRequired: false, + isNullable: false, + inputTypes: [ + { + isList: false, + type: `Boolean`, + location: 'scalar', + }, + ], + })), + }; + modelCountOutputTypeSelectInputObjectTypes.push(modelCountOutputTypeSelectInputObjectType); + } + return modelCountOutputTypeSelectInputObjectTypes; +} + +function generateModelCountOutputTypeArgsInputObjectTypes(modelCountOutputTypes: DMMF.OutputType[]) { + const modelCountOutputTypeArgsInputObjectTypes: DMMF.InputType[] = []; + for (const modelCountOutputType of modelCountOutputTypes) { + const { name: modelCountOutputTypeName } = modelCountOutputType; + const modelCountOutputTypeArgsInputObjectType: DMMF.InputType = { + name: `${modelCountOutputTypeName}Args`, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields: [ + { + name: 'select', + isRequired: false, + isNullable: false, + inputTypes: [ + { + isList: false, + type: `${modelCountOutputTypeName}Select`, + location: 'inputObjectTypes', + namespace: 'prisma', + }, + ], + }, + ], + }; + modelCountOutputTypeArgsInputObjectTypes.push(modelCountOutputTypeArgsInputObjectType); + } + return modelCountOutputTypeArgsInputObjectTypes; +} + +function generateModelSelectInputObjectTypes(models: DMMF.Model[]) { + const modelSelectInputObjectTypes: DMMF.InputType[] = []; + for (const model of models) { + const { name: modelName, fields: modelFields } = model; + const fields: DMMF.SchemaArg[] = []; + + for (const modelField of modelFields) { + const { name: modelFieldName, isList, type } = modelField; + + const isRelationField = checkIsModelRelationField(modelField); + + const field: DMMF.SchemaArg = { + name: modelFieldName, + isRequired: false, + isNullable: false, + inputTypes: [{ isList: false, type: 'Boolean', location: 'scalar' }], + }; + + if (isRelationField) { + const schemaArgInputType: DMMF.SchemaArgInputType = { + isList: false, + type: isList ? `${type}FindManyArgs` : `${type}Args`, + location: 'inputObjectTypes', + namespace: 'prisma', + }; + field.inputTypes.push(schemaArgInputType); + } + + fields.push(field); + } + + const hasManyRelationToAnotherModel = checkModelHasManyModelRelation(model); + + const shouldAddCountField = hasManyRelationToAnotherModel; + if (shouldAddCountField) { + const _countField: DMMF.SchemaArg = { + name: '_count', + isRequired: false, + isNullable: false, + inputTypes: [ + { isList: false, type: 'Boolean', location: 'scalar' }, + { + isList: false, + type: `${modelName}CountOutputTypeArgs`, + location: 'inputObjectTypes', + namespace: 'prisma', + }, + ], + }; + fields.push(_countField); + } + + const modelSelectInputObjectType: DMMF.InputType = { + name: `${modelName}Select`, + constraints: { + maxNumFields: null, + minNumFields: null, + }, + fields, + }; + modelSelectInputObjectTypes.push(modelSelectInputObjectType); + } + return modelSelectInputObjectTypes; +} diff --git a/packages/plugins/trpc/src/zod/index.ts b/packages/plugins/trpc/src/zod/index.ts new file mode 100644 index 000000000..aee2371d4 --- /dev/null +++ b/packages/plugins/trpc/src/zod/index.ts @@ -0,0 +1,10 @@ +import { DMMF } from '@prisma/generator-helper'; +import { PluginOptions } from '@zenstackhq/sdk'; +import { Model } from '@zenstackhq/sdk/ast'; +import { generate } from './generator'; + +export const name = 'Zod'; + +export default async function run(model: Model, options: PluginOptions, dmmf: DMMF.Document) { + return generate(model, options, dmmf); +} diff --git a/packages/plugins/trpc/src/zod/transformer.ts b/packages/plugins/trpc/src/zod/transformer.ts new file mode 100644 index 000000000..de896054a --- /dev/null +++ b/packages/plugins/trpc/src/zod/transformer.ts @@ -0,0 +1,640 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import type { DMMF as PrismaDMMF } from '@prisma/generator-helper'; +import { AUXILIARY_FIELDS } from '@zenstackhq/sdk'; +import indentString from '@zenstackhq/sdk/utils'; +import path from 'path'; +import { checkModelHasModelRelation, findModelByName, isMongodbRawOp } from './helpers'; +import { isAggregateInputType } from './helpers/aggregate-helpers'; +import { AggregateOperationSupport, TransformerParams } from './types'; +import { writeFileSafely } from './utils/writeFileSafely'; + +export default class Transformer { + name: string; + fields: PrismaDMMF.SchemaArg[]; + schemaImports = new Set(); + models: PrismaDMMF.Model[]; + modelOperations: PrismaDMMF.ModelMapping[]; + aggregateOperationSupport: AggregateOperationSupport; + enumTypes: PrismaDMMF.SchemaEnum[]; + + static enumNames: string[] = []; + static rawOpsMap: { [name: string]: string } = {}; + static provider: string; + private static outputPath = './generated'; + private hasJson = false; + private static prismaClientOutputPath = '@prisma/client'; + private static isCustomPrismaClientOutputPath = false; + private static isGenerateSelect = false; + private static isGenerateInclude = false; + + constructor(params: TransformerParams) { + this.name = params.name ?? ''; + this.fields = params.fields ?? []; + this.models = params.models ?? []; + this.modelOperations = params.modelOperations ?? []; + this.aggregateOperationSupport = params.aggregateOperationSupport ?? {}; + this.enumTypes = params.enumTypes ?? []; + } + + static setOutputPath(outPath: string) { + this.outputPath = outPath; + } + + static setIsGenerateSelect(isGenerateSelect: boolean) { + this.isGenerateSelect = isGenerateSelect; + } + + static setIsGenerateInclude(isGenerateInclude: boolean) { + this.isGenerateInclude = isGenerateInclude; + } + + static getOutputPath() { + return this.outputPath; + } + + static setPrismaClientOutputPath(prismaClientCustomPath: string) { + this.prismaClientOutputPath = prismaClientCustomPath; + this.isCustomPrismaClientOutputPath = prismaClientCustomPath !== '@prisma/client'; + } + + async generateEnumSchemas() { + for (const enumType of this.enumTypes) { + const { name, values } = enumType; + + await writeFileSafely( + path.join(Transformer.outputPath, `schemas/enums/${name}.schema.ts`), + `/* eslint-disable */\n${this.generateImportZodStatement()}\n${this.generateExportSchemaStatement( + `${name}`, + `z.enum(${JSON.stringify(values)})` + )}` + ); + } + } + + generateImportZodStatement() { + return "import { z } from 'zod';\n"; + } + + generateExportSchemaStatement(name: string, schema: string) { + return `export const ${name}Schema = ${schema}`; + } + + async generateObjectSchema() { + const zodObjectSchemaFields = this.generateObjectSchemaFields(); + const objectSchema = this.prepareObjectSchema(zodObjectSchemaFields); + const objectSchemaName = this.resolveObjectSchemaName(); + + await writeFileSafely( + path.join(Transformer.outputPath, `schemas/objects/${objectSchemaName}.schema.ts`), + '/* eslint-disable */\n' + objectSchema + ); + } + + generateObjectSchemaFields() { + const zodObjectSchemaFields = this.fields + .filter((field) => !AUXILIARY_FIELDS.includes(field.name)) + .map((field) => this.generateObjectSchemaField(field)) + .flatMap((item) => item) + .map((item) => { + const [zodStringWithMainType, field, skipValidators] = item; + + const value = skipValidators + ? zodStringWithMainType + : this.generateFieldValidators(zodStringWithMainType, field); + + return value.trim(); + }); + return zodObjectSchemaFields; + } + + generateObjectSchemaField(field: PrismaDMMF.SchemaArg): [string, PrismaDMMF.SchemaArg, boolean][] { + const lines = field.inputTypes; + + if (lines.length === 0) { + return []; + } + + let alternatives = lines.reduce((result, inputType) => { + if (inputType.type === 'String') { + result.push(this.wrapWithZodValidators('z.string()', field, inputType)); + } else if (inputType.type === 'Int' || inputType.type === 'Float' || inputType.type === 'Decimal') { + result.push(this.wrapWithZodValidators('z.number()', field, inputType)); + } else if (inputType.type === 'BigInt') { + result.push(this.wrapWithZodValidators('z.bigint()', field, inputType)); + } else if (inputType.type === 'Boolean') { + result.push(this.wrapWithZodValidators('z.boolean()', field, inputType)); + } else if (inputType.type === 'DateTime') { + result.push(this.wrapWithZodValidators('z.date()', field, inputType)); + } else if (inputType.type === 'Json') { + this.hasJson = true; + + result.push(this.wrapWithZodValidators('jsonSchema', field, inputType)); + } else if (inputType.type === 'True') { + result.push(this.wrapWithZodValidators('z.literal(true)', field, inputType)); + } else { + const isEnum = inputType.location === 'enumTypes'; + + if (inputType.namespace === 'prisma' || isEnum) { + if (inputType.type !== this.name && typeof inputType.type === 'string') { + this.addSchemaImport(inputType.type); + } + + result.push(this.generatePrismaStringLine(field, inputType, lines.length)); + } + } + + return result; + }, []); + + if (alternatives.length === 0) { + return []; + } + + if (alternatives.length > 1) { + alternatives = alternatives.map((alter) => alter.replace('.optional()', '')); + } + + const fieldName = alternatives.some((alt) => alt.includes(':')) ? '' : ` ${field.name}:`; + + const opt = !field.isRequired ? '.optional()' : ''; + + let resString = + alternatives.length === 1 ? alternatives.join(',\r\n') : `z.union([${alternatives.join(',\r\n')}])${opt}`; + + if (field.isNullable) { + resString += '.nullable()'; + } + + return [[` ${fieldName} ${resString} `, field, true]]; + } + + wrapWithZodValidators( + mainValidator: string, + field: PrismaDMMF.SchemaArg, + inputType: PrismaDMMF.SchemaArgInputType + ) { + let line = ''; + line = mainValidator; + + if (inputType.isList) { + line += '.array()'; + } + + if (!field.isRequired) { + line += '.optional()'; + } + + return line; + } + + addSchemaImport(name: string) { + this.schemaImports.add(name); + } + + generatePrismaStringLine( + field: PrismaDMMF.SchemaArg, + inputType: PrismaDMMF.SchemaArgInputType, + inputsLength: number + ) { + const isEnum = inputType.location === 'enumTypes'; + + const { isModelQueryType, modelName, queryName } = this.checkIsModelQueryType(inputType.type as string); + + const objectSchemaLine = isModelQueryType + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.resolveModelQuerySchemaName(modelName!, queryName!) + : `${inputType.type}ObjectSchema`; + const enumSchemaLine = `${inputType.type}Schema`; + + const schema = inputType.type === this.name ? objectSchemaLine : isEnum ? enumSchemaLine : objectSchemaLine; + + const arr = inputType.isList ? '.array()' : ''; + + const opt = !field.isRequired ? '.optional()' : ''; + + return inputsLength === 1 + ? ` ${field.name}: z.lazy(() => ${schema})${arr}${opt}` + : `z.lazy(() => ${schema})${arr}${opt}`; + } + + generateFieldValidators(zodStringWithMainType: string, field: PrismaDMMF.SchemaArg) { + const { isRequired, isNullable } = field; + + if (!isRequired) { + zodStringWithMainType += '.optional()'; + } + + if (isNullable) { + zodStringWithMainType += '.nullable()'; + } + + return zodStringWithMainType; + } + + prepareObjectSchema(zodObjectSchemaFields: string[]) { + const objectSchema = `${this.generateExportObjectSchemaStatement( + this.addFinalWrappers({ zodStringFields: zodObjectSchemaFields }) + )}\n`; + + const prismaImportStatement = this.generateImportPrismaStatement(); + + const json = this.generateJsonSchemaImplementation(); + + return `${this.generateObjectSchemaImportStatements()}${prismaImportStatement}${json}${objectSchema}`; + } + + generateExportObjectSchemaStatement(schema: string) { + let name = this.name; + let exportName = this.name; + if (Transformer.provider === 'mongodb') { + if (isMongodbRawOp(name)) { + name = Transformer.rawOpsMap[name]; + exportName = name.replace('Args', ''); + } + } + + if (isAggregateInputType(name)) { + name = `${name}Type`; + } + const end = `export const ${exportName}ObjectSchema = Schema`; + return `const Schema: z.ZodType = ${schema};\n\n ${end}`; + } + + addFinalWrappers({ zodStringFields }: { zodStringFields: string[] }) { + const fields = [...zodStringFields]; + + return this.wrapWithZodObject(fields) + '.strict()'; + } + + generateImportPrismaStatement() { + let prismaClientImportPath: string; + if (Transformer.isCustomPrismaClientOutputPath) { + /** + * If a custom location was designated for the prisma client, we need to figure out the + * relative path from {outputPath}/schemas/objects to {prismaClientCustomPath} + */ + const fromPath = path.join(Transformer.outputPath, 'schemas', 'objects'); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const toPath = Transformer.prismaClientOutputPath!; + const relativePathFromOutputToPrismaClient = path + .relative(fromPath, toPath) + .split(path.sep) + .join(path.posix.sep); + prismaClientImportPath = relativePathFromOutputToPrismaClient; + } else { + /** + * If the default output path for prisma client (@prisma/client) is being used, we can import from it directly + * without having to resolve a relative path + */ + prismaClientImportPath = Transformer.prismaClientOutputPath; + } + return `import type { Prisma } from '${prismaClientImportPath}';\n\n`; + } + + generateJsonSchemaImplementation() { + let jsonSchemaImplementation = ''; + + if (this.hasJson) { + jsonSchemaImplementation += `\n`; + jsonSchemaImplementation += `const literalSchema = z.union([z.string(), z.number(), z.boolean()]);\n`; + jsonSchemaImplementation += `const jsonSchema: z.ZodType = z.lazy(() =>\n`; + jsonSchemaImplementation += ` z.union([literalSchema, z.array(jsonSchema.nullable()), z.record(jsonSchema.nullable())])\n`; + jsonSchemaImplementation += `);\n\n`; + } + + return jsonSchemaImplementation; + } + + generateObjectSchemaImportStatements() { + let generatedImports = this.generateImportZodStatement(); + generatedImports += this.generateSchemaImports(); + generatedImports += '\n\n'; + return generatedImports; + } + + generateSchemaImports() { + return [...this.schemaImports] + .map((name) => { + const { isModelQueryType, modelName } = this.checkIsModelQueryType(name); + if (isModelQueryType) { + return `import { ${modelName}Schema } from '../${modelName}.schema'`; + } else if (Transformer.enumNames.includes(name)) { + return `import { ${name}Schema } from '../enums/${name}.schema'`; + } else { + return `import { ${name}ObjectSchema } from './${name}.schema'`; + } + }) + .join(';\r\n'); + } + + checkIsModelQueryType(type: string) { + const modelQueryTypeSuffixToQueryName: Record = { + FindManyArgs: 'findMany', + }; + for (const modelQueryType of ['FindManyArgs']) { + if (type.includes(modelQueryType)) { + const modelQueryTypeSuffixIndex = type.indexOf(modelQueryType); + return { + isModelQueryType: true, + modelName: type.substring(0, modelQueryTypeSuffixIndex), + queryName: modelQueryTypeSuffixToQueryName[modelQueryType], + }; + } + } + return { isModelQueryType: false }; + } + + resolveModelQuerySchemaName(modelName: string, queryName: string) { + const modelNameCapitalized = modelName.charAt(0).toUpperCase() + modelName.slice(1); + return `${modelNameCapitalized}Schema.${queryName}`; + } + + wrapWithZodUnion(zodStringFields: string[]) { + let wrapped = ''; + + wrapped += 'z.union(['; + wrapped += '\n'; + wrapped += ' ' + zodStringFields.join(','); + wrapped += '\n'; + wrapped += '])'; + return wrapped; + } + + wrapWithZodObject(zodStringFields: string | string[]) { + let wrapped = ''; + + wrapped += 'z.object({'; + wrapped += '\n'; + wrapped += ' ' + zodStringFields; + wrapped += '\n'; + wrapped += '})'; + return wrapped; + } + + resolveObjectSchemaName() { + let name = this.name; + let exportName = this.name; + if (isMongodbRawOp(name)) { + name = Transformer.rawOpsMap[name]; + exportName = name.replace('Args', ''); + } + return exportName; + } + + async generateModelSchemas() { + const globalImports: string[] = []; + let globalExport = ''; + + for (const modelOperation of this.modelOperations) { + const { + model: modelName, + findUnique, + findFirst, + findMany, + // @ts-expect-error + createOne, + createMany, + // @ts-expect-error + deleteOne, + // @ts-expect-error + updateOne, + deleteMany, + updateMany, + // @ts-expect-error + upsertOne, + aggregate, + groupBy, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = modelOperation; + + globalImports.push(`import { ${modelName}Schema } from './${modelName}.schema'`); + globalExport += `${modelName}: ${modelName}Schema,`; + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const model = findModelByName(this.models, modelName)!; + + const { + selectImport, + includeImport, + selectZodSchemaLine, + includeZodSchemaLine, + selectZodSchemaLineLazy, + includeZodSchemaLineLazy, + } = this.resolveSelectIncludeImportAndZodSchemaLine(model); + + let imports = [`import { z } from 'zod'`, selectImport, includeImport]; + let codeBody = ''; + + if (findUnique) { + imports.push( + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'` + ); + codeBody += `findUnique: z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} where: ${modelName}WhereUniqueInputObjectSchema }),`; + } + + if (findFirst) { + imports.push( + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'`, + `import { ${modelName}OrderByWithRelationInputObjectSchema } from './objects/${modelName}OrderByWithRelationInput.schema'`, + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'`, + `import { ${modelName}ScalarFieldEnumSchema } from './enums/${modelName}ScalarFieldEnum.schema'` + ); + codeBody += `findFirst: z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} where: ${modelName}WhereInputObjectSchema.optional(), orderBy: z.union([${modelName}OrderByWithRelationInputObjectSchema, ${modelName}OrderByWithRelationInputObjectSchema.array()]).optional(), cursor: ${modelName}WhereUniqueInputObjectSchema.optional(), take: z.number().optional(), skip: z.number().optional(), distinct: z.array(${modelName}ScalarFieldEnumSchema).optional() }),`; + } + + if (findMany) { + imports.push( + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'`, + `import { ${modelName}OrderByWithRelationInputObjectSchema } from './objects/${modelName}OrderByWithRelationInput.schema'`, + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'`, + `import { ${modelName}ScalarFieldEnumSchema } from './enums/${modelName}ScalarFieldEnum.schema'` + ); + codeBody += `findMany: z.object({ ${selectZodSchemaLineLazy} ${includeZodSchemaLineLazy} where: ${modelName}WhereInputObjectSchema.optional(), orderBy: z.union([${modelName}OrderByWithRelationInputObjectSchema, ${modelName}OrderByWithRelationInputObjectSchema.array()]).optional(), cursor: ${modelName}WhereUniqueInputObjectSchema.optional(), take: z.number().optional(), skip: z.number().optional(), distinct: z.array(${modelName}ScalarFieldEnumSchema).optional() }),`; + } + + if (createOne) { + imports.push( + `import { ${modelName}CreateInputObjectSchema } from './objects/${modelName}CreateInput.schema'` + ); + codeBody += `create: z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} data: ${modelName}CreateInputObjectSchema }),`; + } + + if (createMany) { + imports.push( + `import { ${modelName}CreateManyInputObjectSchema } from './objects/${modelName}CreateManyInput.schema'` + ); + codeBody += `createMany: z.object({ data: ${modelName}CreateManyInputObjectSchema }),`; + } + + if (deleteOne) { + imports.push( + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'` + ); + codeBody += `'delete': z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} where: ${modelName}WhereUniqueInputObjectSchema }),`; + } + + if (deleteMany) { + imports.push( + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'` + ); + codeBody += `deleteMany: z.object({ where: ${modelName}WhereInputObjectSchema.optional() }),`; + } + + if (updateOne) { + imports.push( + `import { ${modelName}UpdateInputObjectSchema } from './objects/${modelName}UpdateInput.schema'`, + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'` + ); + codeBody += `update: z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} data: ${modelName}UpdateInputObjectSchema, where: ${modelName}WhereUniqueInputObjectSchema }),`; + } + + if (updateMany) { + imports.push( + `import { ${modelName}UpdateManyMutationInputObjectSchema } from './objects/${modelName}UpdateManyMutationInput.schema'`, + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'` + ); + codeBody += `updateMany: z.object({ data: ${modelName}UpdateManyMutationInputObjectSchema, where: ${modelName}WhereInputObjectSchema.optional() }),`; + } + + if (upsertOne) { + imports.push( + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'`, + `import { ${modelName}CreateInputObjectSchema } from './objects/${modelName}CreateInput.schema'`, + `import { ${modelName}UpdateInputObjectSchema } from './objects/${modelName}UpdateInput.schema'` + ); + codeBody += `upsert: z.object({ ${selectZodSchemaLine} ${includeZodSchemaLine} where: ${modelName}WhereUniqueInputObjectSchema, create: ${modelName}CreateInputObjectSchema, update: ${modelName}UpdateInputObjectSchema }),`; + } + + if (aggregate) { + imports.push( + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'`, + `import { ${modelName}OrderByWithRelationInputObjectSchema } from './objects/${modelName}OrderByWithRelationInput.schema'`, + `import { ${modelName}WhereUniqueInputObjectSchema } from './objects/${modelName}WhereUniqueInput.schema'` + ); + const aggregateOperations = []; + if (this.aggregateOperationSupport[modelName].count) { + imports.push( + `import { ${modelName}CountAggregateInputObjectSchema } from './objects/${modelName}CountAggregateInput.schema'` + ); + aggregateOperations.push( + `_count: z.union([ z.literal(true), ${modelName}CountAggregateInputObjectSchema ]).optional()` + ); + } + if (this.aggregateOperationSupport[modelName].min) { + imports.push( + `import { ${modelName}MinAggregateInputObjectSchema } from './objects/${modelName}MinAggregateInput.schema'` + ); + aggregateOperations.push(`_min: ${modelName}MinAggregateInputObjectSchema.optional()`); + } + if (this.aggregateOperationSupport[modelName].max) { + imports.push( + `import { ${modelName}MaxAggregateInputObjectSchema } from './objects/${modelName}MaxAggregateInput.schema'` + ); + aggregateOperations.push(`_max: ${modelName}MaxAggregateInputObjectSchema.optional()`); + } + if (this.aggregateOperationSupport[modelName].avg) { + imports.push( + `import { ${modelName}AvgAggregateInputObjectSchema } from './objects/${modelName}AvgAggregateInput.schema'` + ); + aggregateOperations.push(`_avg: ${modelName}AvgAggregateInputObjectSchema.optional()`); + } + if (this.aggregateOperationSupport[modelName].sum) { + imports.push( + `import { ${modelName}SumAggregateInputObjectSchema } from './objects/${modelName}SumAggregateInput.schema'` + ); + aggregateOperations.push(`_sum: ${modelName}SumAggregateInputObjectSchema.optional()`); + } + + codeBody += `aggregate: z.object({ where: ${modelName}WhereInputObjectSchema.optional(), orderBy: z.union([${modelName}OrderByWithRelationInputObjectSchema, ${modelName}OrderByWithRelationInputObjectSchema.array()]).optional(), cursor: ${modelName}WhereUniqueInputObjectSchema.optional(), take: z.number().optional(), skip: z.number().optional(), ${aggregateOperations.join( + ', ' + )} }),`; + } + + if (groupBy) { + imports.push( + `import { ${modelName}WhereInputObjectSchema } from './objects/${modelName}WhereInput.schema'`, + `import { ${modelName}OrderByWithAggregationInputObjectSchema } from './objects/${modelName}OrderByWithAggregationInput.schema'`, + `import { ${modelName}ScalarWhereWithAggregatesInputObjectSchema } from './objects/${modelName}ScalarWhereWithAggregatesInput.schema'`, + `import { ${modelName}ScalarFieldEnumSchema } from './enums/${modelName}ScalarFieldEnum.schema'` + ); + codeBody += `groupBy: z.object({ where: ${modelName}WhereInputObjectSchema.optional(), orderBy: z.union([${modelName}OrderByWithAggregationInputObjectSchema, ${modelName}OrderByWithAggregationInputObjectSchema.array()]), having: ${modelName}ScalarWhereWithAggregatesInputObjectSchema.optional(), take: z.number().optional(), skip: z.number().optional(), by: z.array(${modelName}ScalarFieldEnumSchema) }),`; + } + + imports = [...new Set(imports)]; + + await writeFileSafely( + path.join(Transformer.outputPath, `schemas/${modelName}.schema.ts`), + ` +/* eslint-disable */ +${imports.join(';\n')} + +export const ${modelName}Schema = { +${indentString(codeBody, 4)} +}; + ` + ); + } + + await writeFileSafely( + path.join(Transformer.outputPath, 'schemas/index.ts'), + ` +/* eslint-disable */ +${globalImports.join(';\n')} + +const schemas = { +${indentString(globalExport, 4)} +}; + +export default schemas; +` + ); + } + + generateImportStatements(imports: (string | undefined)[]) { + let generatedImports = this.generateImportZodStatement(); + generatedImports += imports?.filter((importItem) => !!importItem).join(';\r\n') ?? ''; + generatedImports += '\n\n'; + return generatedImports; + } + + resolveSelectIncludeImportAndZodSchemaLine(model: PrismaDMMF.Model) { + const { name: modelName } = model; + + const hasRelationToAnotherModel = checkModelHasModelRelation(model); + + const selectImport = Transformer.isGenerateSelect + ? `import { ${modelName}SelectObjectSchema } from './objects/${modelName}Select.schema'` + : ''; + + const includeImport = + Transformer.isGenerateInclude && hasRelationToAnotherModel + ? `import { ${modelName}IncludeObjectSchema } from './objects/${modelName}Include.schema'` + : ''; + + let selectZodSchemaLine = ''; + let includeZodSchemaLine = ''; + let selectZodSchemaLineLazy = ''; + let includeZodSchemaLineLazy = ''; + + if (Transformer.isGenerateSelect) { + const zodSelectObjectSchema = `${modelName}SelectObjectSchema.optional()`; + selectZodSchemaLine = `select: ${zodSelectObjectSchema},`; + selectZodSchemaLineLazy = `select: z.lazy(() => ${zodSelectObjectSchema}),`; + } + + if (Transformer.isGenerateInclude && hasRelationToAnotherModel) { + const zodIncludeObjectSchema = `${modelName}IncludeObjectSchema.optional()`; + includeZodSchemaLine = `include: ${zodIncludeObjectSchema},`; + includeZodSchemaLineLazy = `include: z.lazy(() => ${zodIncludeObjectSchema}),`; + } + + return { + selectImport, + includeImport, + selectZodSchemaLine, + includeZodSchemaLine, + selectZodSchemaLineLazy, + includeZodSchemaLineLazy, + }; + } +} diff --git a/packages/plugins/trpc/src/zod/types.ts b/packages/plugins/trpc/src/zod/types.ts new file mode 100644 index 000000000..a02b9ca7c --- /dev/null +++ b/packages/plugins/trpc/src/zod/types.ts @@ -0,0 +1,22 @@ +import { DMMF as PrismaDMMF } from '@prisma/generator-helper'; + +export type TransformerParams = { + enumTypes?: PrismaDMMF.SchemaEnum[]; + fields?: PrismaDMMF.SchemaArg[]; + name?: string; + models?: PrismaDMMF.Model[]; + modelOperations?: PrismaDMMF.ModelMapping[]; + aggregateOperationSupport?: AggregateOperationSupport; + isDefaultPrismaClientOutput?: boolean; + prismaClientOutputPath?: string; +}; + +export type AggregateOperationSupport = { + [model: string]: { + count?: boolean; + min?: boolean; + max?: boolean; + sum?: boolean; + avg?: boolean; + }; +}; diff --git a/packages/plugins/trpc/src/zod/utils/formatFile.ts b/packages/plugins/trpc/src/zod/utils/formatFile.ts new file mode 100644 index 000000000..11c151ad9 --- /dev/null +++ b/packages/plugins/trpc/src/zod/utils/formatFile.ts @@ -0,0 +1,31 @@ +import prettier from 'prettier'; + +export const formatFile = (content: string): Promise => { + return new Promise((res, rej) => + prettier.resolveConfig(process.cwd()).then((options) => { + let formatOptions = options; + if (!options) { + formatOptions = { + trailingComma: 'all', + tabWidth: 2, + printWidth: 80, + bracketSpacing: true, + semi: true, + singleQuote: true, + useTabs: false, + }; + } + + try { + const formatted = prettier.format(content, { + ...formatOptions, + parser: 'typescript', + }); + + res(formatted); + } catch (error) { + rej(error); + } + }) + ); +}; diff --git a/packages/plugins/trpc/src/zod/utils/removeDir.ts b/packages/plugins/trpc/src/zod/utils/removeDir.ts new file mode 100644 index 000000000..03f8d74f5 --- /dev/null +++ b/packages/plugins/trpc/src/zod/utils/removeDir.ts @@ -0,0 +1,15 @@ +import path from 'path'; +import { promises as fs } from 'fs'; + +export default async function removeDir(dirPath: string, onlyContent: boolean) { + const dirEntries = await fs.readdir(dirPath, { withFileTypes: true }); + await Promise.all( + dirEntries.map(async (dirEntry) => { + const fullPath = path.join(dirPath, dirEntry.name); + return dirEntry.isDirectory() ? await removeDir(fullPath, false) : await fs.unlink(fullPath); + }) + ); + if (!onlyContent) { + await fs.rmdir(dirPath); + } +} diff --git a/packages/plugins/trpc/src/zod/utils/writeFileSafely.ts b/packages/plugins/trpc/src/zod/utils/writeFileSafely.ts new file mode 100644 index 000000000..0f953a2a5 --- /dev/null +++ b/packages/plugins/trpc/src/zod/utils/writeFileSafely.ts @@ -0,0 +1,11 @@ +import fs from 'fs'; +import path from 'path'; +import { formatFile } from './formatFile'; + +export const writeFileSafely = async (writeLocation: string, content: string) => { + fs.mkdirSync(path.dirname(writeLocation), { + recursive: true, + }); + + fs.writeFileSync(writeLocation, await formatFile(content)); +}; diff --git a/packages/plugins/trpc/tsconfig.json b/packages/plugins/trpc/tsconfig.json new file mode 100644 index 000000000..e2c5bd0f3 --- /dev/null +++ b/packages/plugins/trpc/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "CommonJS", + "lib": ["ESNext", "DOM"], + "sourceMap": true, + "outDir": "dist", + "strict": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "resolveJsonModule": true, + "strictPropertyInitialization": false, + "paths": {} + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/runtime/LICENSE b/packages/runtime/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/packages/runtime/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/runtime/package.json b/packages/runtime/package.json index e731e21c5..82fe4c3ba 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/runtime", "displayName": "ZenStack Runtime Library", - "version": "0.5.0", + "version": "1.0.0-alpha.27", "description": "Runtime of ZenStack for both client-side and server-side environments.", "repository": { "type": "git", @@ -9,7 +9,7 @@ }, "scripts": { "clean": "rimraf dist", - "build": "pnpm lint && pnpm clean && tsc && cp -r pre/* dist/ && cp ./package.json ./README.md dist/", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ../../LICENSE dist", "watch": "tsc --watch", "lint": "eslint src --ext ts", "prepublishOnly": "pnpm build", @@ -21,7 +21,9 @@ }, "dependencies": { "@types/bcryptjs": "^2.4.2", + "@zenstackhq/sdk": "workspace:*", "bcryptjs": "^2.4.3", + "change-case": "^4.1.2", "colors": "1.4.0", "cuid": "^2.1.8", "decimal.js": "^10.4.2", @@ -33,6 +35,7 @@ "zod-validation-error": "^0.2.1" }, "peerDependencies": { + "@prisma/client": "^4.0.0", "next": "^12.3.1 || ^13", "react": "^17.0.2 || ^18", "react-dom": "^17.0.2 || ^18" @@ -46,6 +49,7 @@ "@types/bcryptjs": "^2.4.2", "@types/jest": "^29.0.3", "@types/node": "^14.18.29", + "copyfiles": "^2.4.1", "rimraf": "^3.0.2", "typescript": "^4.9.3" } diff --git a/packages/runtime/pre/client/index.d.ts b/packages/runtime/pre/client/index.d.ts deleted file mode 100644 index 14f8c9dbc..000000000 --- a/packages/runtime/pre/client/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from '.zenstack/lib/hooks'; -export { HooksError, ServerErrorCode, RequestOptions } from '../lib/types'; -export * as request from '../lib/request'; -export * from '../lib/validation'; diff --git a/packages/runtime/pre/client/index.js b/packages/runtime/pre/client/index.js deleted file mode 100644 index ba2969596..000000000 --- a/packages/runtime/pre/client/index.js +++ /dev/null @@ -1,12 +0,0 @@ -// needed for importing from client-side code -Object.defineProperty(exports, '__esModule', { value: true }); - -const request = require('../lib/request'); -const types = require('../lib/types'); - -module.exports = { - ...require('.zenstack/lib/hooks'), - ...require('../lib/validation'), - ServerErrorCode: types.ServerErrorCode, - request, -}; diff --git a/packages/runtime/pre/server/auth.d.ts b/packages/runtime/pre/server/auth.d.ts deleted file mode 100644 index 279619b14..000000000 --- a/packages/runtime/pre/server/auth.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '.zenstack/lib/auth'; diff --git a/packages/runtime/pre/server/auth.js b/packages/runtime/pre/server/auth.js deleted file mode 100644 index d8348f834..000000000 --- a/packages/runtime/pre/server/auth.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('.zenstack/lib/auth'), -}; diff --git a/packages/runtime/pre/server/index.d.ts b/packages/runtime/pre/server/index.d.ts deleted file mode 100644 index 76946d7b4..000000000 --- a/packages/runtime/pre/server/index.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import service from '.zenstack/lib'; - -export type { - FieldInfo, - PolicyKind, - PolicyOperationKind, - RuntimeAttribute, - QueryContext, - Service, - DbClientContract, -} from '../lib/types'; - -export { - requestHandler, - type RequestHandlerOptions, -} from '../lib/request-handler'; - -export default service; diff --git a/packages/runtime/pre/server/index.js b/packages/runtime/pre/server/index.js deleted file mode 100644 index 93b5e3896..000000000 --- a/packages/runtime/pre/server/index.js +++ /dev/null @@ -1,7 +0,0 @@ -Object.defineProperty(exports, '__esModule', { value: true }); - -exports.default = require('.zenstack/lib').default; - -const exportStar = require('tslib').__exportStar; -exportStar(require('../lib/types'), exports); -exportStar(require('../lib/request-handler'), exports); diff --git a/packages/runtime/pre/types/index.d.ts b/packages/runtime/pre/types/index.d.ts deleted file mode 100644 index b96c699d5..000000000 --- a/packages/runtime/pre/types/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '.zenstack/.prisma'; diff --git a/packages/runtime/pre/types/index.js b/packages/runtime/pre/types/index.js deleted file mode 100644 index a638f1a17..000000000 --- a/packages/runtime/pre/types/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('.zenstack/.prisma'), -}; diff --git a/packages/runtime/src/config.ts b/packages/runtime/src/config.ts deleted file mode 100644 index dd50cd1ae..000000000 --- a/packages/runtime/src/config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { LogLevel } from './types'; - -/** - * Logging config definition - */ -export type LogDefinition = { - level: LogLevel; - emit: 'stdout' | 'event'; -}; - -/** - * Service configuration - */ -export interface ServiceConfig { - log?: Array; -} diff --git a/packages/runtime/src/constants.ts b/packages/runtime/src/constants.ts index df4072b8a..be7c688ef 100644 --- a/packages/runtime/src/constants.ts +++ b/packages/runtime/src/constants.ts @@ -1,13 +1,3 @@ -/** - * Auxiliary database field for supporting policy check for nested writes - */ -export const TRANSACTION_FIELD_NAME = 'zenstack_transaction'; - -/** - * Auxiliary database field for building up policy check queries - */ -export const GUARD_FIELD_NAME = 'zenstack_guard'; - /** * Default length of password hash salt (used by bcryptjs to hash password) */ diff --git a/packages/runtime/src/enhancements/index.ts b/packages/runtime/src/enhancements/index.ts new file mode 100644 index 000000000..df26a1a7f --- /dev/null +++ b/packages/runtime/src/enhancements/index.ts @@ -0,0 +1,4 @@ +export * from './omit'; +export * from './password'; +export * from './policy'; +export * from './preset'; diff --git a/packages/runtime/src/enhancements/model-meta.ts b/packages/runtime/src/enhancements/model-meta.ts new file mode 100644 index 000000000..719880f15 --- /dev/null +++ b/packages/runtime/src/enhancements/model-meta.ts @@ -0,0 +1,21 @@ +import { camelCase } from 'change-case'; +import { ModelMeta } from './types'; + +/** + * Load model meta from standard location. + */ +export function getDefaultModelMeta(): ModelMeta { + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return require('.zenstack/model-meta').default; + } catch { + throw new Error('Model meta cannot be loaded'); + } +} + +/** + * Resolves a model field to its metadata. Returns undefined if not found. + */ +export function resolveField(modelMeta: ModelMeta, model: string, field: string) { + return modelMeta.fields[camelCase(model)][field]; +} diff --git a/packages/runtime/src/enhancements/nested-write-vistor.ts b/packages/runtime/src/enhancements/nested-write-vistor.ts new file mode 100644 index 000000000..59e1e7dcd --- /dev/null +++ b/packages/runtime/src/enhancements/nested-write-vistor.ts @@ -0,0 +1,221 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { FieldInfo, PrismaWriteActionType, PrismaWriteActions } from '../types'; +import { resolveField } from './model-meta'; +import { ModelMeta } from './types'; +import { Enumerable, ensureArray, getModelFields } from './utils'; + +/** + * Context for visiting + */ +export type VisitorContext = { + /** + * Parent data, can be used to replace fields + */ + parent: any; + + /** + * Current field, undefined if toplevel + */ + field?: FieldInfo; + + /** + * A top-down path of all nested update conditions and corresponding field till now + */ + nestingPath: { field?: FieldInfo; where: any }[]; +}; + +/** + * NestedWriteVisitor's callback actions + */ +export type NestedWriterVisitorCallback = { + create?: (model: string, args: any[], context: VisitorContext) => Promise; + + connectOrCreate?: ( + model: string, + args: Enumerable<{ where: object; create: any }>, + context: VisitorContext + ) => Promise; + + update?: (model: string, args: Enumerable<{ where: object; data: any }>, context: VisitorContext) => Promise; + + updateMany?: ( + model: string, + args: Enumerable<{ where?: object; data: any }>, + context: VisitorContext + ) => Promise; + + upsert?: ( + model: string, + args: Enumerable<{ where: object; create: any; update: any }>, + context: VisitorContext + ) => Promise; + + delete?: (model: string, args: Enumerable | boolean, context: VisitorContext) => Promise; + + deleteMany?: (model: string, args: Enumerable, context: VisitorContext) => Promise; + + field?: (field: FieldInfo, action: PrismaWriteActionType, data: any, context: VisitorContext) => Promise; +}; + +/** + * Recursive visitor for nested write (create/update) payload + */ +export class NestedWriteVisitor { + constructor(private readonly modelMeta: ModelMeta, private readonly callback: NestedWriterVisitorCallback) {} + + private isPrismaWriteAction(value: string): value is PrismaWriteActionType { + return PrismaWriteActions.includes(value as PrismaWriteActionType); + } + + /** + * Start visiting + * + * @see NestedWriterVisitorCallback + */ + async visit(model: string, action: PrismaWriteActionType, args: any): Promise { + if (!args) { + return; + } + + let topData = args; + switch (action) { + // create has its data wrapped in 'data' field + case 'create': + topData = topData.data; + break; + + case 'delete': + case 'deleteMany': + topData = topData.where; + break; + } + + await this.doVisit(model, action, topData, undefined, undefined, []); + } + + private async doVisit( + model: string, + action: PrismaWriteActionType, + data: any, + parent: any, + field: FieldInfo | undefined, + nestingPath: { field?: FieldInfo; where: any }[] + ): Promise { + if (!data) { + return; + } + + const fieldContainers: any[] = []; + const isToOneUpdate = field?.isDataModel && !field.isArray; + const context = { parent, field, nestingPath: [...nestingPath] }; + + // visit payload + switch (action) { + case 'create': + context.nestingPath.push({ field, where: {} }); + if (this.callback.create) { + await this.callback.create(model, data, context); + } + fieldContainers.push(...ensureArray(data)); + break; + + case 'createMany': + // skip the 'data' layer so as to keep consistency with 'create' + if (data.data) { + context.nestingPath.push({ field, where: {} }); + if (this.callback.create) { + await this.callback.create(model, data.data, context); + } + fieldContainers.push(...ensureArray(data.data)); + } + break; + + case 'connectOrCreate': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.connectOrCreate) { + await this.callback.connectOrCreate(model, data, context); + } + fieldContainers.push(...ensureArray(data).map((d) => d.create)); + break; + + case 'update': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.update) { + await this.callback.update(model, data, context); + } + fieldContainers.push(...ensureArray(data).map((d) => (isToOneUpdate ? d : d.data))); + break; + + case 'updateMany': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.updateMany) { + await this.callback.updateMany(model, data, context); + } + fieldContainers.push(...ensureArray(data)); + break; + + case 'upsert': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.upsert) { + await this.callback.upsert(model, data, context); + } + fieldContainers.push(...ensureArray(data).map((d) => d.create)); + fieldContainers.push(...ensureArray(data).map((d) => d.update)); + break; + + case 'delete': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.delete) { + await this.callback.delete(model, data, context); + } + break; + + case 'deleteMany': + context.nestingPath.push({ field, where: data.where }); + if (this.callback.deleteMany) { + await this.callback.deleteMany(model, data, context); + } + break; + + default: { + throw new Error(`unhandled action type ${action}`); + } + } + + for (const fieldContainer of fieldContainers) { + for (const field of getModelFields(fieldContainer)) { + const fieldInfo = resolveField(this.modelMeta, model, field); + if (!fieldInfo) { + continue; + } + + if (fieldInfo.isDataModel) { + // recurse into nested payloads + for (const [subAction, subData] of Object.entries(fieldContainer[field])) { + if (this.isPrismaWriteAction(subAction) && subData) { + await this.doVisit( + fieldInfo.type, + subAction, + subData, + fieldContainer[field], + fieldInfo, + nestingPath + ); + } + } + } else { + // visit plain field + if (this.callback.field) { + await this.callback.field(fieldInfo, action, fieldContainer[field], { + parent: fieldContainer, + nestingPath: nestingPath, + field: fieldInfo, + }); + } + } + } + } + } +} diff --git a/packages/runtime/src/enhancements/omit.ts b/packages/runtime/src/enhancements/omit.ts new file mode 100644 index 000000000..7f0de58d5 --- /dev/null +++ b/packages/runtime/src/enhancements/omit.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { DbClientContract } from '../types'; +import { getDefaultModelMeta, resolveField } from './model-meta'; +import { DefaultPrismaProxyHandler, makeProxy } from './proxy'; +import { ModelMeta } from './types'; +import { ensureArray, getModelFields } from './utils'; + +/** + * Gets an enhanced Prisma client that supports @omit attribute. + */ +export function withOmit(prisma: DbClient, modelMeta?: ModelMeta): DbClient { + const _modelMeta = modelMeta ?? getDefaultModelMeta(); + return makeProxy( + prisma, + _modelMeta, + (_prisma, model) => new OmitHandler(_prisma as DbClientContract, model, _modelMeta), + 'omit' + ); +} + +class OmitHandler extends DefaultPrismaProxyHandler { + constructor(prisma: DbClientContract, model: string, private readonly modelMeta: ModelMeta) { + super(prisma, model); + } + + // base override + protected async processResultEntity(data: T): Promise { + if (data) { + for (const value of ensureArray(data)) { + await this.doPostProcess(value, this.model); + } + } + return data; + } + + private async doPostProcess(entityData: any, model: string) { + for (const field of getModelFields(entityData)) { + const fieldInfo = await resolveField(this.modelMeta, model, field); + if (!fieldInfo) { + continue; + } + + if (fieldInfo.attributes.find((attr) => attr.name === '@omit')) { + delete entityData[field]; + } else if (fieldInfo.isDataModel) { + // recurse + await this.doPostProcess(entityData[field], fieldInfo.type); + } + } + } +} diff --git a/packages/runtime/src/enhancements/password.ts b/packages/runtime/src/enhancements/password.ts new file mode 100644 index 000000000..2688cb965 --- /dev/null +++ b/packages/runtime/src/enhancements/password.ts @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { hash } from 'bcryptjs'; +import { DEFAULT_PASSWORD_SALT_LENGTH } from '../constants'; +import { DbClientContract, PrismaWriteActionType } from '../types'; +import { getDefaultModelMeta } from './model-meta'; +import { NestedWriteVisitor } from './nested-write-vistor'; +import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy'; +import { ModelMeta } from './types'; + +/** + * Gets an enhanced Prisma client that supports @password attribute. + */ +export function withPassword(prisma: DbClient, modelMeta?: ModelMeta): DbClient { + const _modelMeta = modelMeta ?? getDefaultModelMeta(); + return makeProxy( + prisma, + _modelMeta, + (_prisma, model) => new PasswordHandler(_prisma as DbClientContract, model, _modelMeta), + 'password' + ); +} + +class PasswordHandler extends DefaultPrismaProxyHandler { + constructor(prisma: DbClientContract, model: string, readonly modelMeta: ModelMeta) { + super(prisma, model); + } + + // base override + protected async preprocessArgs(action: PrismaProxyActions, args: any) { + const actionsOfInterest: PrismaProxyActions[] = ['create', 'createMany', 'update', 'updateMany', 'upsert']; + if (args && args.data && actionsOfInterest.includes(action)) { + await this.preprocessWritePayload(this.model, action as PrismaWriteActionType, args); + } + return args; + } + + private async preprocessWritePayload(model: string, action: PrismaWriteActionType, args: any) { + const visitor = new NestedWriteVisitor(this.modelMeta, { + field: async (field, _action, data, context) => { + const pwdAttr = field.attributes?.find((attr) => attr.name === '@password'); + if (pwdAttr && field.type === 'String') { + // hash password value + let salt: string | number | undefined = pwdAttr.args.find((arg) => arg.name === 'salt') + ?.value as string; + if (!salt) { + salt = pwdAttr.args.find((arg) => arg.name === 'saltLength')?.value as number; + } + if (!salt) { + salt = DEFAULT_PASSWORD_SALT_LENGTH; + } + context.parent[field.name] = await hash(data, salt); + } + }, + }); + + await visitor.visit(model, action, args); + } +} diff --git a/packages/runtime/src/enhancements/policy/handler.ts b/packages/runtime/src/enhancements/policy/handler.ts new file mode 100644 index 000000000..9cf982ad0 --- /dev/null +++ b/packages/runtime/src/enhancements/policy/handler.ts @@ -0,0 +1,298 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { PrismaClientValidationError } from '@prisma/client/runtime'; +import { format } from 'util'; +import { AuthUser, DbClientContract, PolicyOperationKind } from '../../types'; +import { BatchResult, PrismaProxyHandler } from '../proxy'; +import { ModelMeta, PolicyDef } from '../types'; +import { Logger } from './logger'; +import { PolicyUtil } from './policy-utils'; + +/** + * Prisma proxy handler for injecting access policy check. + */ +export class PolicyProxyHandler implements PrismaProxyHandler { + private readonly logger: Logger; + private readonly utils: PolicyUtil; + + constructor( + private readonly prisma: DbClient, + private readonly policy: PolicyDef, + private readonly modelMeta: ModelMeta, + private readonly model: string, + private readonly user?: AuthUser + ) { + this.logger = new Logger(prisma); + this.utils = new PolicyUtil(this.prisma, this.modelMeta, this.policy, this.user); + } + + private get modelClient() { + return this.prisma[this.model]; + } + + async findUnique(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.where) { + throw new PrismaClientValidationError('where field is required in query argument'); + } + + const entities = await this.utils.readWithCheck(this.model, args); + return entities[0] ?? null; + } + + async findUniqueOrThrow(args: any) { + const entity = await this.findUnique(args); + if (!entity) { + throw this.utils.notFound(this.model); + } + return entity; + } + + async findFirst(args: any) { + const entities = await this.utils.readWithCheck(this.model, args); + return entities[0] ?? null; + } + + async findFirstOrThrow(args: any) { + const entity = await this.findFirst(args); + if (!entity) { + throw this.utils.notFound(this.model); + } + return entity; + } + + async findMany(args: any) { + return this.utils.readWithCheck(this.model, args); + } + + async create(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.data) { + throw new PrismaClientValidationError('data field is required in query argument'); + } + + await this.tryReject('create'); + + const origArgs = args; + args = this.utils.clone(args); + + // use a transaction to wrap the write so it can be reverted if the created + // entity fails access policies + const result: any = await this.utils.processWrite(this.model, 'create', args, (dbOps, writeArgs) => + dbOps.create(writeArgs) + ); + + if (!this.utils.getEntityId(this.model, result)) { + throw this.utils.unknownError(`unexpected error: create didn't return an id`); + } + + return this.checkReadback(origArgs, this.utils.getEntityId(this.model, result), 'create', 'create'); + } + + async createMany(args: any, skipDuplicates?: boolean) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.data) { + throw new PrismaClientValidationError('data field is required and must be an array'); + } + + await this.tryReject('create'); + + args = this.utils.clone(args); + + // use a transaction to wrap the write so it can be reverted if any created + // entity fails access policies + const result = await this.utils.processWrite(this.model, 'create', args, (dbOps, writeArgs) => + dbOps.createMany(writeArgs, skipDuplicates) + ); + + return result as BatchResult; + } + + async update(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.where) { + throw new PrismaClientValidationError('where field is required in query argument'); + } + if (!args.data) { + throw new PrismaClientValidationError('data field is required in query argument'); + } + + await this.tryReject('update'); + + const origArgs = args; + args = this.utils.clone(args); + + // use a transaction to wrap the write so it can be reverted if any nested + // create fails access policies + const result: any = await this.utils.processWrite(this.model, 'update', args, (dbOps, writeArgs) => + dbOps.update(writeArgs) + ); + + if (!this.utils.getEntityId(this.model, result)) { + throw this.utils.unknownError(`unexpected error: update didn't return an id`); + } + return this.checkReadback(origArgs, this.utils.getEntityId(this.model, result), 'update', 'update'); + } + + async updateMany(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.data) { + throw new PrismaClientValidationError('data field is required in query argument'); + } + + await this.tryReject('update'); + + args = this.utils.clone(args); + + // use a transaction to wrap the write so it can be reverted if any nested + // create fails access policies + const result = await this.utils.processWrite(this.model, 'updateMany', args, (dbOps, writeArgs) => + dbOps.updateMany(writeArgs) + ); + + return result as BatchResult; + } + + async upsert(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.where) { + throw new PrismaClientValidationError('where field is required in query argument'); + } + if (!args.create) { + throw new PrismaClientValidationError('create field is required in query argument'); + } + if (!args.update) { + throw new PrismaClientValidationError('update field is required in query argument'); + } + + const origArgs = args; + args = this.utils.clone(args); + + await this.tryReject('create'); + await this.tryReject('update'); + + // use a transaction to wrap the write so it can be reverted if any nested + // create fails access policies + const result: any = await this.utils.processWrite(this.model, 'upsert', args, (dbOps, writeArgs) => + dbOps.upsert(writeArgs) + ); + + if (!this.utils.getEntityId(this.model, result)) { + throw this.utils.unknownError(`unexpected error: upsert didn't return an id`); + } + + return this.checkReadback(origArgs, this.utils.getEntityId(this.model, result), 'upsert', 'update'); + } + + async delete(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + if (!args.where) { + throw new PrismaClientValidationError('where field is required in query argument'); + } + + await this.tryReject('delete'); + + // ensures the item under deletion passes policy check + await this.utils.checkPolicyForFilter(this.model, args.where, 'delete', this.prisma); + + // read the entity under deletion with respect to read policies + let readResult: any; + try { + const items = await this.utils.readWithCheck(this.model, args); + readResult = items[0]; + } catch (err) { + // not readable + readResult = undefined; + } + + // conduct the deletion + this.logger.info(`Conducting delete ${this.model}:\n${format(args)}`); + await this.modelClient.delete(args); + + if (!readResult) { + throw this.utils.deniedByPolicy(this.model, 'delete', 'result not readable'); + } else { + return readResult; + } + } + + async deleteMany(args: any) { + await this.tryReject('delete'); + + // inject policy conditions + args = args ?? {}; + await this.utils.injectAuthGuard(args, this.model, 'delete'); + + // conduct the deletion + this.logger.info(`Conducting deleteMany ${this.model}:\n${format(args)}`); + return this.modelClient.deleteMany(args); + } + + async aggregate(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + + await this.tryReject('read'); + + // inject policy conditions + await this.utils.injectAuthGuard(args, this.model, 'read'); + return this.modelClient.aggregate(args); + } + + async groupBy(args: any) { + if (!args) { + throw new PrismaClientValidationError('query argument is required'); + } + + await this.tryReject('read'); + + // inject policy conditions + await this.utils.injectAuthGuard(args, this.model, 'read'); + + return this.modelClient.groupBy(args); + } + + async count(args: any) { + await this.tryReject('read'); + + // inject policy conditions + args = args ?? {}; + await this.utils.injectAuthGuard(args, this.model, 'read'); + return this.modelClient.count(args); + } + + async tryReject(operation: PolicyOperationKind) { + const guard = await this.utils.getAuthGuard(this.model, operation); + if (guard === false) { + throw this.utils.deniedByPolicy(this.model, operation); + } + } + + private async checkReadback(origArgs: any, id: any, action: string, operation: PolicyOperationKind) { + const idField = this.utils.getIdField(this.model); + const readArgs = { select: origArgs.select, include: origArgs.include, where: { [idField.name]: id } }; + const result = await this.utils.readWithCheck(this.model, readArgs); + if (result.length === 0) { + this.logger.warn(`${action} result cannot be read back`); + throw this.utils.deniedByPolicy(this.model, operation, 'result not readable'); + } else if (result.length > 1) { + throw this.utils.unknownError('write unexpected resulted in multiple readback entities'); + } + return result[0]; + } +} diff --git a/packages/runtime/src/enhancements/policy/index.ts b/packages/runtime/src/enhancements/policy/index.ts new file mode 100644 index 000000000..e54495cb9 --- /dev/null +++ b/packages/runtime/src/enhancements/policy/index.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { AuthUser, DbClientContract } from '../../types'; +import { getDefaultModelMeta } from '../model-meta'; +import { makeProxy } from '../proxy'; +import { ModelMeta, PolicyDef } from '../types'; +import { PolicyProxyHandler } from './handler'; + +/** + * Context for evaluating access policies + */ +export type WithPolicyContext = { + user?: AuthUser; +}; + +/** + * Gets an enhanced Prisma client with access policy check. + * + * @param prisma The original Prisma client + * @param context The policy evaluation context + * @param policy The policy definition, will be loaded from default location if not provided + * @param modelMeta The model metadata, will be loaded from default location if not provided + */ +export function withPolicy( + prisma: DbClient, + context?: WithPolicyContext, + policy?: PolicyDef, + modelMeta?: ModelMeta +): DbClient { + const _policy = policy ?? getDefaultPolicy(); + const _modelMeta = modelMeta ?? getDefaultModelMeta(); + return makeProxy( + prisma, + _modelMeta, + (_prisma, model) => + new PolicyProxyHandler(_prisma as DbClientContract, _policy, _modelMeta, model, context?.user), + 'policy' + ); +} + +function getDefaultPolicy(): PolicyDef { + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return require('.zenstack/policy').default; + } catch { + throw new Error('Policy definition cannot be loaded from default location'); + } +} diff --git a/packages/runtime/src/enhancements/policy/logger.ts b/packages/runtime/src/enhancements/policy/logger.ts new file mode 100644 index 000000000..a3e7d7fbf --- /dev/null +++ b/packages/runtime/src/enhancements/policy/logger.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { EventEmitter } from 'stream'; + +/** + * A logger that uses an existing Prisma client to emit. + */ +export class Logger { + constructor(private readonly prisma: any) {} + + private get emitter() { + const engine = (this.prisma as any).getEngine(); + return engine ? (engine.logEmitter as EventEmitter) : undefined; + } + + public log(level: 'info' | 'warn' | 'error', message: string) { + this.emitter?.emit(level, { + timestamp: new Date(), + message, + target: 'zenstack', + }); + } + + /** + * Generates a log message with info level. + */ + public info(message: string) { + this.log('info', message); + } + + /** + * Generates a log message with warn level. + */ + public warn(message: string) { + this.log('warn', message); + } + + /** + * Generates a log message with error level. + */ + public error(message: string) { + this.log('error', message); + } +} diff --git a/packages/runtime/src/enhancements/policy/policy-utils.ts b/packages/runtime/src/enhancements/policy/policy-utils.ts new file mode 100644 index 000000000..4afee5791 --- /dev/null +++ b/packages/runtime/src/enhancements/policy/policy-utils.ts @@ -0,0 +1,642 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { PrismaClientKnownRequestError, PrismaClientUnknownRequestError } from '@prisma/client/runtime'; +import { TRANSACTION_FIELD_NAME, AUXILIARY_FIELDS } from '@zenstackhq/sdk'; +import { camelCase } from 'change-case'; +import cuid from 'cuid'; +import deepcopy from 'deepcopy'; +import { format } from 'util'; +import { fromZodError } from 'zod-validation-error'; +import { + AuthUser, + DbClientContract, + DbOperations, + FieldInfo, + PolicyOperationKind, + PrismaWriteActionType, +} from '../../types'; +import { getVersion } from '../../version'; +import { resolveField } from '../model-meta'; +import { NestedWriteVisitor, VisitorContext } from '../nested-write-vistor'; +import { ModelMeta, PolicyDef, PolicyFunc } from '../types'; +import { enumerate, getModelFields } from '../utils'; +import { Logger } from './logger'; + +/** + * Access policy enforcement utilities + */ +export class PolicyUtil { + private readonly logger: Logger; + + constructor( + private readonly db: DbClientContract, + private readonly modelMeta: ModelMeta, + private readonly policy: PolicyDef, + private readonly user?: AuthUser + ) { + this.logger = new Logger(db); + } + + /** + * Creates a conjunction of a list of query conditions. + */ + and(...conditions: (boolean | object)[]): any { + if (conditions.includes(false)) { + // always false + return { id: { in: [] } }; + } + + const filtered = conditions.filter( + (c): c is object => typeof c === 'object' && !!c && Object.keys(c).length > 0 + ); + if (filtered.length === 0) { + return undefined; + } else if (filtered.length === 1) { + return filtered[0]; + } else { + return { AND: filtered }; + } + } + + /** + * Creates a disjunction of a list of query conditions. + */ + or(...conditions: (boolean | object)[]): any { + if (conditions.includes(true)) { + // always true + return { id: { notIn: [] } }; + } + + const filtered = conditions.filter((c): c is object => typeof c === 'object' && !!c); + if (filtered.length === 0) { + return undefined; + } else if (filtered.length === 1) { + return filtered[0]; + } else { + return { OR: filtered }; + } + } + + /** + * Gets pregenerated authorization guard object for a given model and operation. + * + * @returns true if operation is unconditionally allowed, false if unconditionally denied, + * otherwise returns a guard object + */ + async getAuthGuard(model: string, operation: PolicyOperationKind, preValue?: any): Promise { + const guard = this.policy.guard[camelCase(model)]; + if (!guard) { + throw this.unknownError(`unable to load policy guard for ${model}`); + } + + const provider: PolicyFunc | boolean | undefined = guard[operation]; + if (typeof provider === 'boolean') { + return provider; + } + + if (!provider) { + throw this.unknownError(`zenstack: unable to load authorization guard for ${model}`); + } + return provider({ user: this.user, preValue }); + } + + private async getPreValueSelect(model: string): Promise { + const guard = this.policy.guard[camelCase(model)]; + if (!guard) { + throw this.unknownError(`unable to load policy guard for ${model}`); + } + return guard.preValueSelect; + } + + private async getModelSchema(model: string) { + return this.policy.schema[camelCase(model)]; + } + + /** + * Injects model auth guard as where clause. + */ + async injectAuthGuard(args: any, model: string, operation: PolicyOperationKind) { + const guard = await this.getAuthGuard(model, operation); + args.where = this.and(args.where, guard); + } + + /** + * Read model entities w.r.t the given query args. The result list + * are guaranteed to fully satisfy 'read' policy rules recursively. + * + * For to-many relations involved, items not satisfying policy are + * silently trimmed. For to-one relation, if relation data fails policy + * an error is thrown. + */ + async readWithCheck(model: string, args: any): Promise { + args = this.clone(args); + await this.injectAuthGuard(args, model, 'read'); + + // recursively inject read guard conditions into the query args + await this.injectNestedReadConditions(model, args); + + this.logger.info(`Reading with validation for ${model}: ${format(args)}`); + const result: any[] = await this.db[model].findMany(args); + + await Promise.all(result.map((item) => this.postProcessForRead(item, model, args, 'read'))); + + return result; + } + + private async injectNestedReadConditions(model: string, args: any) { + const injectTarget = args.select ?? args.include; + if (!injectTarget) { + return; + } + + const idField = this.getIdField(model); + for (const field of getModelFields(injectTarget)) { + const fieldInfo = resolveField(this.modelMeta, model, field); + if (!fieldInfo || !fieldInfo.isDataModel) { + // only care about relation fields + continue; + } + + if (fieldInfo.isArray) { + if (typeof injectTarget[field] !== 'object') { + injectTarget[field] = {}; + } + // inject extra condition for to-many relation + const guard = await this.getAuthGuard(fieldInfo.type, 'read'); + injectTarget[field].where = this.and(injectTarget.where, guard); + } else { + // there's no way of injecting condition for to-one relation, so we + // make sure 'id' field is selected and check them against query result + if (injectTarget[field]?.select && injectTarget[field]?.select?.[idField.name] !== true) { + injectTarget[field].select[idField.name] = true; + } + } + + // recurse + await this.injectNestedReadConditions(fieldInfo.type, injectTarget[field]); + } + } + + /** + * Post processing checks for read model entities. Validates to-one relations + * (which can't be trimmed at query time) and removes fields that should be + * omitted. + */ + async postProcessForRead(entityData: any, model: string, args: any, operation: PolicyOperationKind) { + if (!this.getEntityId(model, entityData)) { + return; + } + + // strip auxiliary fields + for (const auxField of AUXILIARY_FIELDS) { + if (auxField in entityData) { + delete entityData[auxField]; + } + } + + const injectTarget = args.select ?? args.include; + if (!injectTarget) { + return; + } + + // to-one relation data cannot be trimmed by injected guards, we have to + // post-check them + + for (const field of getModelFields(injectTarget)) { + const fieldInfo = resolveField(this.modelMeta, model, field); + if (!fieldInfo || !fieldInfo.isDataModel || fieldInfo.isArray) { + continue; + } + + const idField = this.getIdField(fieldInfo.type); + const relatedEntityId = entityData?.[field]?.[idField.name]; + + if (!relatedEntityId) { + continue; + } + + this.logger.info(`Validating read of to-one relation: ${fieldInfo.type}#${relatedEntityId}`); + + await this.checkPolicyForFilter(fieldInfo.type, { [idField.name]: relatedEntityId }, operation, this.db); + + // recurse + await this.postProcessForRead(entityData[field], fieldInfo.type, injectTarget[field], operation); + } + } + + /** + * Process Prisma write actions. + */ + async processWrite( + model: string, + action: PrismaWriteActionType, + args: any, + writeAction: (dbOps: DbOperations, writeArgs: any) => Promise + ) { + // record model types for which new entities are created + // so we can post-check if they satisfy 'create' policies + const createdModels = new Set(); + + // record model entities that are updated, together with their + // values before update, so we can post-check if they satisfy + // model => id => entity value + const updatedModels = new Map>(); + + const idField = this.getIdField(model); + if (args.select && !args.select[idField.name]) { + // make sure 'id' field is selected, we need it to + // read back the updated entity + args.select[idField.name] = true; + } + + // use a transaction to conduct write, so in case any create or nested create + // fails access policies, we can roll back the entire operation + const transactionId = cuid(); + + // args processor for create + const processCreate = async (model: string, args: any) => { + const guard = await this.getAuthGuard(model, 'create'); + const schema = await this.getModelSchema(model); + if (guard === false) { + throw this.deniedByPolicy(model, 'create'); + } else if (guard !== true || schema) { + // mark the create with a transaction tag so we can check them later + args[TRANSACTION_FIELD_NAME] = `${transactionId}:create`; + createdModels.add(model); + } + }; + + // build a reversed query for fetching entities affected by nested updates + const buildReversedQuery = async (context: VisitorContext) => { + let result, currQuery: any; + let currField: FieldInfo | undefined; + + for (let i = context.nestingPath.length - 1; i >= 0; i--) { + const { field, where } = context.nestingPath[i]; + + if (!result) { + // first segment (bottom), just use its where clause + result = currQuery = { ...where }; + currField = field; + } else { + if (!currField) { + throw this.unknownError(`missing field in nested path`); + } + if (!currField.backLink) { + throw this.unknownError(`field ${currField.type}.${currField.name} doesn't have a backLink`); + } + currQuery[currField.backLink] = { ...where }; + currQuery = currQuery[currField.backLink]; + currField = field; + } + } + return result; + }; + + // args processor for update/upsert + const processUpdate = async (model: string, args: any, context: VisitorContext) => { + const preGuard = await this.getAuthGuard(model, 'update'); + if (preGuard === false) { + throw this.deniedByPolicy(model, 'update'); + } else if (preGuard !== true) { + if (this.isToOneRelation(context.field)) { + // To-one relation field is complicated because there's no way to + // filter it during update (args doesn't carry a 'where' clause). + // + // We need to recursively walk up its hierarcy in the query args + // to construct a reversed query to identify the nested entity + // under update, and then check if it satisfies policy. + // + // E.g.: + // A - B - C + // + // update A with: + // { + // where: { id: 'aId' }, + // data: { + // b: { + // c: { value: 1 } + // } + // } + // } + // + // To check if the update to 'c' field is permitted, we + // reverse the query stack into a filter for C model, like: + // { + // where: { + // b: { a: { id: 'aId' } } + // } + // } + // , and with this we can filter out the C entity that's going + // to be nestedly updated, and check if it's allowed. + // + // The same logic applies to nested delete. + + const subQuery = await buildReversedQuery(context); + await this.checkPolicyForFilter(model, subQuery, 'update', this.db); + } else { + // non-nested update, check policies directly + if (!args.where) { + throw this.unknownError(`Missing 'where' in update args`); + } + await this.checkPolicyForFilter(model, args.where, 'update', this.db); + } + } + + await preparePostUpdateCheck(model, context); + }; + + // args processor for updateMany + const processUpdateMany = async (model: string, args: any, context: VisitorContext) => { + const guard = await this.getAuthGuard(model, 'update'); + if (guard === false) { + throw this.deniedByPolicy(model, 'update'); + } else if (guard !== true) { + // inject policy filter + await this.injectAuthGuard(args, model, 'update'); + } + + await preparePostUpdateCheck(model, context); + }; + + // for models with post-update rules, we need to read and store + // entity values before the update for post-update check + const preparePostUpdateCheck = async (model: string, context: VisitorContext) => { + const postGuard = await this.getAuthGuard(model, 'postUpdate'); + const schema = await this.getModelSchema(model); + + // post-update check is needed if there's post-update rule or validation schema + if (postGuard !== true || schema) { + let modelEntities = updatedModels.get(model); + if (!modelEntities) { + modelEntities = new Map(); + updatedModels.set(model, modelEntities); + } + + // fetch preValue selection (analyzed from the post-update rules) + const preValueSelect = await this.getPreValueSelect(model); + const filter = await buildReversedQuery(context); + const idField = this.getIdField(model); + const query = { where: filter, select: { ...preValueSelect, [idField.name]: true } }; + this.logger.info(`fetching pre-update entities for ${model}: ${format(query)})}`); + const entities = await this.db[model].findMany(query); + entities.forEach((entity) => modelEntities?.set(this.getEntityId(model, entity), entity)); + } + }; + + // args processor for delete + const processDelete = async (model: string, args: any, context: VisitorContext) => { + const guard = await this.getAuthGuard(model, 'delete'); + if (guard === false) { + throw this.deniedByPolicy(model, 'delete'); + } else if (guard !== true) { + if (this.isToOneRelation(context.field)) { + // see comments in processUpdate + const subQuery = await buildReversedQuery(context); + await this.checkPolicyForFilter(model, subQuery, 'delete', this.db); + } else { + await this.checkPolicyForFilter(model, args, 'delete', this.db); + } + } + }; + + // use a visitor to process args before conducting the write action + const visitor = new NestedWriteVisitor(this.modelMeta, { + create: async (model, args) => { + for (const oneArgs of enumerate(args)) { + await processCreate(model, oneArgs); + } + }, + + connectOrCreate: async (model, args) => { + for (const oneArgs of enumerate(args)) { + if (oneArgs.create) { + await processCreate(model, oneArgs.create); + } + } + }, + + update: async (model, args, context) => { + for (const oneArgs of enumerate(args)) { + await processUpdate(model, oneArgs, context); + } + }, + + updateMany: async (model, args, context) => { + for (const oneArgs of enumerate(args)) { + await processUpdateMany(model, oneArgs, context); + } + }, + + upsert: async (model, args, context) => { + for (const oneArgs of enumerate(args)) { + if (oneArgs.create) { + await processCreate(model, oneArgs.create); + } + + if (oneArgs.update) { + await processUpdate(model, { where: oneArgs.where, data: oneArgs.update }, context); + } + } + }, + + delete: async (model, args, context) => { + for (const oneArgs of enumerate(args)) { + await processDelete(model, oneArgs, context); + } + }, + + deleteMany: async (model, args, context) => { + const guard = await this.getAuthGuard(model, 'delete'); + if (guard === false) { + throw this.deniedByPolicy(model, 'delete'); + } else if (guard !== true) { + if (Array.isArray(args)) { + context.parent.deleteMany = args.map((oneArgs) => this.and(oneArgs, guard)); + } else { + context.parent.deleteMany = this.and(args, guard); + } + } + }, + }); + + await visitor.visit(model, action, args); + + if (createdModels.size === 0 && updatedModels.size === 0) { + // no post-check needed, we can proceed with the write without transaction + return await writeAction(this.db[model], args); + } else { + return await this.transaction(this.db, async (tx) => { + // proceed with the update (with args processed) + const result = await writeAction(tx[model], args); + + if (createdModels.size > 0) { + // do post-check on created entities + await Promise.all( + [...createdModels].map((model) => + this.checkPolicyForFilter( + model, + { [TRANSACTION_FIELD_NAME]: `${transactionId}:create` }, + 'create', + tx + ) + ) + ); + } + + if (updatedModels.size > 0) { + // do post-check on updated entities + await Promise.all( + [...updatedModels.entries()] + .map(([model, modelEntities]) => + [...modelEntities.entries()].map(async ([id, preValue]) => + this.checkPostUpdate(model, id, tx, preValue) + ) + ) + .flat() + ); + } + + return result; + }); + } + } + + private transaction(db: DbClientContract, action: (tx: Record) => Promise) { + if (db.__zenstack_tx) { + // already in transaction, don't nest + return action(db); + } else { + return db.$transaction((tx) => action(tx)); + } + } + + deniedByPolicy(model: string, operation: PolicyOperationKind, extra?: string) { + return new PrismaClientKnownRequestError( + `denied by policy: ${model} entities failed '${operation}' check${extra ? ', ' + extra : ''}`, + { clientVersion: getVersion(), code: 'P2004' } + ); + } + + notFound(model: string) { + return new PrismaClientKnownRequestError(`entity not found for model ${model}`, { + clientVersion: getVersion(), + code: 'P2025', + }); + } + + unknownError(message: string) { + return new PrismaClientUnknownRequestError(message, { + clientVersion: getVersion(), + }); + } + + /** + * Given a filter, check if applying access policy filtering will result + * in data being trimmed, and if so, throw an error. + */ + async checkPolicyForFilter( + model: string, + filter: any, + operation: PolicyOperationKind, + db: Record + ) { + this.logger.info(`Checking policy for ${model}#${JSON.stringify(filter)} for ${operation}`); + + const count = (await db[model].count({ where: filter })) as number; + const guard = await this.getAuthGuard(model, operation); + + // build a query condition with policy injected + const guardedQuery = { where: this.and(filter, guard) }; + + const schema = (operation === 'create' || operation === 'update') && (await this.getModelSchema(model)); + + if (schema) { + // we've got schemas, so have to fetch entities and validate them + const entities = await db[model].findMany(guardedQuery); + if (entities.length < count) { + this.logger.info(`entity ${model} failed policy check for operation ${operation}`); + throw this.deniedByPolicy(model, operation, `${count - entities.length} entities failed policy check`); + } + + // TODO: push down schema check to the database + const schemaCheckErrors = entities.map((entity) => schema.safeParse(entity)).filter((r) => !r.success); + if (schemaCheckErrors.length > 0) { + const error = schemaCheckErrors.map((r) => !r.success && fromZodError(r.error).message).join(', '); + this.logger.info(`entity ${model} failed schema check for operation ${operation}: ${error}`); + throw this.deniedByPolicy(model, operation, `entities failed schema check: [${error}]`); + } + } else { + // count entities with policy injected and see if any of them are filtered out + const guardedCount = (await db[model].count(guardedQuery)) as number; + if (guardedCount < count) { + this.logger.info(`entity ${model} failed policy check for operation ${operation}`); + throw this.deniedByPolicy(model, operation, `${count - guardedCount} entities failed policy check`); + } + } + } + + private async checkPostUpdate(model: string, id: any, db: Record, preValue: any) { + this.logger.info(`Checking post-update policy for ${model}#${id}, preValue: ${format(preValue)}`); + + const guard = await this.getAuthGuard(model, 'postUpdate', preValue); + + // build a query condition with policy injected + const idField = this.getIdField(model); + const guardedQuery = { where: this.and({ [idField.name]: id }, guard) }; + + // query with policy injected + const entity = await db[model].findFirst(guardedQuery); + + // see if we get fewer items with policy, if so, reject with an throw + if (!entity) { + this.logger.info(`entity ${model} failed policy check for operation postUpdate`); + throw this.deniedByPolicy(model, 'postUpdate'); + } + + // TODO: push down schema check to the database + const schema = await this.getModelSchema(model); + if (schema) { + const schemaCheckResult = schema.safeParse(entity); + if (!schemaCheckResult.success) { + const error = fromZodError(schemaCheckResult.error).message; + this.logger.info(`entity ${model} failed schema check for operation postUpdate: ${error}`); + throw this.deniedByPolicy(model, 'postUpdate', `entity failed schema check: ${error}`); + } + } + } + + private isToOneRelation(field: FieldInfo | undefined) { + return !!field && field.isDataModel && !field.isArray; + } + + /** + * Clones an object and makes sure it's not empty. + */ + clone(value: unknown) { + return value ? deepcopy(value) : {}; + } + + /** + * Gets "id" field for a given model. + */ + getIdField(model: string) { + const fields = this.modelMeta.fields[camelCase(model)]; + if (!fields) { + throw this.unknownError(`Unable to load fields for ${model}`); + } + const result = Object.values(fields).find((f) => f.isId); + if (!result) { + throw this.unknownError(`model ${model} does not have an id field`); + } + return result; + } + + /** + * Gets id field value from an entity. + */ + getEntityId(model: string, entityData: any) { + const idField = this.getIdField(model); + return entityData[idField.name]; + } +} diff --git a/packages/runtime/src/enhancements/preset.ts b/packages/runtime/src/enhancements/preset.ts new file mode 100644 index 000000000..e6764a56e --- /dev/null +++ b/packages/runtime/src/enhancements/preset.ts @@ -0,0 +1,26 @@ +import { withOmit } from './omit'; +import { withPassword } from './password'; +import { withPolicy, WithPolicyContext } from './policy'; +import { ModelMeta, PolicyDef } from './types'; + +/** + * Gets a Prisma client enhanced with all essential behaviors, including access + * policy, field validation, field omission and password hashing. + * + * It's a shortcut for calling withOmit(withPassword(withPolicy(prisma, options))). + * + * @param prisma The Prisma client to enhance. + * @param context The context to for evaluating access policies. + * @param policy The access policy data, generated by @zenstack/access-policy plugin. + * You only need to pass it if you configured the plugin to generate into custom location. + * @param modelMeta The model metadata, generated by @zenstack/model-meta plugin. + * You only need to pass it if you configured the plugin to generate into custom location. + */ +export function withPresets( + prisma: DbClient, + context?: WithPolicyContext, + policy?: PolicyDef, + modelMeta?: ModelMeta +) { + return withPolicy(withOmit(withPassword(prisma, modelMeta), modelMeta), context, policy, modelMeta); +} diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts new file mode 100644 index 000000000..19c799fee --- /dev/null +++ b/packages/runtime/src/enhancements/proxy.ts @@ -0,0 +1,224 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { DbClientContract } from '../types'; +import { ModelMeta } from './types'; + +/** + * Prisma batch write operation result + */ +export type BatchResult = { count: number }; + +/** + * Interface for proxy that intercepts Prisma operations. + */ +export interface PrismaProxyHandler { + findUnique(args: any): Promise; + + findUniqueOrThrow(args: any): Promise; + + findFirst(args: any): Promise; + + findFirstOrThrow(args: any): Promise; + + findMany(args: any): Promise; + + create(args: any): Promise; + + createMany(args: any, skipDuplicates?: boolean): Promise; + + update(args: any): Promise; + + updateMany(args: any): Promise; + + upsert(args: any): Promise; + + delete(args: any): Promise; + + deleteMany(args: any): Promise; + + aggregate(args: any): Promise; + + groupBy(args: any): Promise; + + count(args: any): Promise; +} + +/** + * All Prisma operation names + */ +export type PrismaProxyActions = keyof PrismaProxyHandler; + +/** + * A default implementation of @see PrismaProxyHandler which directly + * delegates to the wrapped Prisma client. It offers a few overridable + * methods to allow more easily inject custom logic. + */ +export class DefaultPrismaProxyHandler implements PrismaProxyHandler { + constructor(protected readonly prisma: DbClientContract, protected readonly model: string) {} + + async findUnique(args: any): Promise { + args = await this.preprocessArgs('findUnique', args); + const r = await this.prisma[this.model].findUnique(args); + return this.processResultEntity(r); + } + + async findUniqueOrThrow(args: any): Promise { + args = await this.preprocessArgs('findUniqueOrThrow', args); + const r = await this.prisma[this.model].findUniqueOrThrow(args); + return this.processResultEntity(r); + } + + async findFirst(args: any): Promise { + args = await this.preprocessArgs('findFirst', args); + const r = this.prisma[this.model].findFirst(args); + return this.processResultEntity(r); + } + + async findFirstOrThrow(args: any): Promise { + args = await this.preprocessArgs('findFirstOrThrow', args); + const r = await this.prisma[this.model].findFirstOrThrow(args); + return this.processResultEntity(r); + } + + async findMany(args: any): Promise { + args = await this.preprocessArgs('findMany', args); + const r = await this.prisma[this.model].findMany(args); + return this.processResultEntity(r); + } + + async create(args: any): Promise { + args = await this.preprocessArgs('create', args); + const r = await this.prisma[this.model].create(args); + return this.processResultEntity(r); + } + + async createMany(args: any, skipDuplicates?: boolean | undefined): Promise<{ count: number }> { + args = await this.preprocessArgs('createMany', args); + return this.prisma[this.model].createMany(args, skipDuplicates); + } + + async update(args: any): Promise { + args = await this.preprocessArgs('update', args); + const r = this.prisma[this.model].update(args); + return this.processResultEntity(r); + } + + async updateMany(args: any): Promise<{ count: number }> { + args = await this.preprocessArgs('updateMany', args); + return this.prisma[this.model].updateMany(args); + } + + async upsert(args: any): Promise { + args = await this.preprocessArgs('upsert', args); + const r = this.prisma[this.model].upsert(args); + return this.processResultEntity(r); + } + + async delete(args: any): Promise { + args = await this.preprocessArgs('delete', args); + const r = this.prisma[this.model].delete(args); + return this.processResultEntity(r); + } + + async deleteMany(args: any): Promise<{ count: number }> { + args = await this.preprocessArgs('deleteMany', args); + return this.prisma[this.model].deleteMany(args); + } + + async aggregate(args: any): Promise { + args = await this.preprocessArgs('aggregate', args); + return this.prisma[this.model].aggregate(args); + } + + async groupBy(args: any): Promise { + args = await this.preprocessArgs('groupBy', args); + return this.prisma[this.model].groupBy(args); + } + + async count(args: any): Promise { + args = await this.preprocessArgs('count', args); + return this.prisma[this.model].count(args); + } + + /** + * Processes result entities before they're returned + */ + protected async processResultEntity(data: T): Promise { + return data; + } + + /** + * Processes query args before they're passed to Prisma. + */ + protected async preprocessArgs(method: PrismaProxyActions, args: any) { + return args; + } +} + +/** + * Makes a Prisma client proxy. + */ +export function makeProxy( + prisma: any, + modelMeta: ModelMeta, + makeHandler: (prisma: object, model: string) => T, + name = 'unnamed_enhancer', + inTransaction = false +) { + const models = Object.keys(modelMeta.fields); + const proxy = new Proxy(prisma, { + get: (target: any, prop: string | symbol, receiver: any) => { + // enhancer metadata + if (prop === '__zenstack_enhancer') { + return name; + } + + // transaction metadata + if (prop === '__zenstack_tx') { + return inTransaction; + } + + if (prop === '$transaction') { + // for interactive transactions, we need to proxy the transaction function so that + // when it runs the callback, it provides a proxy to the Prisma client wrapped with + // the same handler + // + // TODO: batch transaction is not supported yet, how? + const $transaction = Reflect.get(target, prop, receiver); + if ($transaction) { + return (input: any, ...rest: any[]) => { + if (Array.isArray(input)) { + throw new Error( + 'Sequential operations transaction is not supported by ZenStack enhanced Prisma client. Please use interactive transaction instead.' + ); + } else if (typeof input !== 'function') { + throw new Error('A function value input is expected'); + } + + const txFunc = input; + return $transaction.bind(target)((tx: any) => { + const txProxy = makeProxy(tx, modelMeta, makeHandler, name + '$tx', true); + return txFunc(txProxy); + }, ...rest); + }; + } else { + return $transaction; + } + } + + if (typeof prop !== 'string' || prop.startsWith('$') || !models.includes(prop)) { + // skip non-model fields + return Reflect.get(target, prop, receiver); + } + + const propVal = Reflect.get(target, prop, receiver); + if (!propVal) { + return undefined; + } + + return makeHandler(target, prop); + }, + }); + + return proxy; +} diff --git a/packages/runtime/src/enhancements/types.ts b/packages/runtime/src/enhancements/types.ts new file mode 100644 index 000000000..73364e7cc --- /dev/null +++ b/packages/runtime/src/enhancements/types.ts @@ -0,0 +1,31 @@ +import { z } from 'zod'; +import { FieldInfo, PolicyOperationKind, QueryContext } from '../types'; + +/** + * ZModel data model metadata + */ +export type ModelMeta = { fields: Record> }; + +/** + * Function for getting policy guard with a given context + */ +export type PolicyFunc = (context: QueryContext) => object; + +/** + * Policy definition + */ +export type PolicyDef = { + // Prisma query guards + guard: Record< + string, + { + allowAll?: boolean; + denyAll?: boolean; + } & Partial> & { + preValueSelect?: object; + } + >; + + // zod schema for post-write validation + schema: Record; +}; diff --git a/packages/runtime/src/enhancements/utils.ts b/packages/runtime/src/enhancements/utils.ts new file mode 100644 index 000000000..f8d2d46c9 --- /dev/null +++ b/packages/runtime/src/enhancements/utils.ts @@ -0,0 +1,31 @@ +import { AUXILIARY_FIELDS } from '@zenstackhq/sdk'; + +/** + * Wraps a value into array if it's not already one + */ +export function ensureArray(value: T): T[] { + return Array.isArray(value) ? value : [value]; +} + +/** + * Gets field names in a data model entity, filtering out internal fields. + */ +export function getModelFields(data: object) { + return Object.keys(data).filter((f) => !AUXILIARY_FIELDS.includes(f)); +} + +/** + * Array or scalar + */ +export type Enumerable = T | Array; + +/** + * Uniformly enumerates an array or scalar. + */ +export function enumerate(x: Enumerable) { + if (Array.isArray(x)) { + return x; + } else { + return [x]; + } +} diff --git a/packages/runtime/src/error.ts b/packages/runtime/src/error.ts new file mode 100644 index 000000000..ecde96a8d --- /dev/null +++ b/packages/runtime/src/error.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export function isPrismaClientKnownRequestError(err: any): err is { code: string; message: string } { + return err.__proto__.constructor.name === 'PrismaClientKnownRequestError'; +} + +export function isPrismaClientUnknownRequestError(err: any): err is { message: string } { + return err.__proto__.constructor.name === 'PrismaClientUnknownRequestError'; +} + +export function isPrismaClientValidationError(err: any): err is { message: string } { + return err.__proto__.constructor.name === 'PrismaClientValidationError'; +} diff --git a/packages/runtime/src/handler/data/crud.ts b/packages/runtime/src/handler/data/crud.ts deleted file mode 100644 index a2a999785..000000000 --- a/packages/runtime/src/handler/data/crud.ts +++ /dev/null @@ -1,457 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import cuid from 'cuid'; -import superjson from 'superjson'; -import { TRANSACTION_FIELD_NAME } from '../../constants'; -import { - DbClientContract, - DbOperations, - getServerErrorMessage, - QueryContext, - ServerErrorCode, - Service, -} from '../../types'; -import { ValidationError } from '../../validation'; -import { CRUDError } from '../types'; -import { - and, - checkPolicyForIds, - injectTransactionId, - preprocessWritePayload, - preUpdateCheck, - queryIds, - readWithCheck, -} from './policy-utils'; - -const PRISMA_ERROR_MAPPING: Record = { - P2002: ServerErrorCode.UNIQUE_CONSTRAINT_VIOLATION, - P2003: ServerErrorCode.REFERENCE_CONSTRAINT_VIOLATION, - P2025: ServerErrorCode.REFERENCE_CONSTRAINT_VIOLATION, -}; - -/** - * Request handler for /data endpoint which processes data CRUD requests. - */ -export class CRUD { - constructor(private readonly service: Service) {} - - private get db() { - return this.service.db as DbClientContract; - } - - async get( - model: string, - id: string, - args: any, - context: QueryContext - ): Promise { - args = args ?? {}; - args.where = and(args.where, { id }); - - let entities: unknown[]; - try { - entities = await readWithCheck( - model, - args, - this.service, - context, - this.db - ); - } catch (err) { - throw this.processError(err, 'get', model); - } - - return entities[0]; - } - - async find( - model: string, - args: any, - context: QueryContext - ): Promise { - try { - return await readWithCheck( - model, - args ?? {}, - this.service, - context, - this.db - ); - } catch (err) { - throw this.processError(err, 'find', model); - } - } - - async create( - model: string, - args: any, - context: QueryContext - ): Promise { - if (!args) { - throw new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - 'body is required' - ); - } - if (!args.data) { - throw new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - 'data field is required' - ); - } - - let createResult: { id: string }; - - try { - await this.service.validateModelPayload(model, 'create', args.data); - - // preprocess payload to modify fields as required by attribute like @password - await preprocessWritePayload(model, args, this.service); - - const transactionId = cuid(); - - // start an interactive transaction - createResult = await this.db.$transaction( - async (tx: Record) => { - // inject transaction id into update/create payload (direct and nested) - const { createdModels } = await injectTransactionId( - model, - args, - 'create', - transactionId, - this.service - ); - - // conduct the create - this.service.verbose( - `Conducting create: ${model}:\n${superjson.stringify( - args - )}` - ); - const createResult = (await tx[model].create(args)) as { - id: string; - }; - - // verify that the created entity pass policy check - await checkPolicyForIds( - model, - [createResult.id], - 'create', - this.service, - context, - tx - ); - - // verify that nested creates pass policy check - await Promise.all( - createdModels.map(async (model) => { - const createdIds = await queryIds(model, tx, { - [TRANSACTION_FIELD_NAME]: `${transactionId}:create`, - }); - this.service.verbose( - `Validating nestedly created entities: ${model}#[${createdIds.join( - ', ' - )}]` - ); - await checkPolicyForIds( - model, - createdIds, - 'create', - this.service, - context, - tx - ); - }) - ); - - return createResult; - } - ); - } catch (err) { - throw this.processError(err, 'create', model); - } - - // verify that return data requested by query args pass policy check - const readArgs = { ...args, where: { id: createResult.id } }; - delete readArgs.data; - - try { - const result = await readWithCheck( - model, - readArgs, - this.service, - context, - this.db - ); - if (result.length === 0) { - throw new CRUDError( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ); - } - return result[0]; - } catch (err) { - if ( - err instanceof CRUDError && - err.code === ServerErrorCode.DENIED_BY_POLICY - ) { - throw new CRUDError( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ); - } else { - throw err; - } - } - } - - async update( - model: string, - id: string, - args: any, - context: QueryContext - ): Promise { - if (!args) { - throw new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - 'body is required' - ); - } - - try { - await this.service.validateModelPayload(model, 'update', args.data); - - // preprocess payload to modify fields as required by attribute like @password - await preprocessWritePayload(model, args, this.service); - - args.where = { ...args.where, id }; - - const transactionId = cuid(); - - await this.db.$transaction( - async (tx: Record) => { - // make sure the entity (including ones involved in nested write) pass policy check - await preUpdateCheck( - model, - id, - args, - this.service, - context, - tx - ); - - // inject transaction id into update/create payload (direct and nested) - const { createdModels } = await injectTransactionId( - model, - args, - 'update', - transactionId, - this.service - ); - - // conduct the update - this.service.verbose( - `Conducting update: ${model}:\n${superjson.stringify( - args - )}` - ); - await tx[model].update(args); - - // verify that nested creates pass policy check - await Promise.all( - createdModels.map(async (model) => { - const createdIds = await queryIds(model, tx, { - [TRANSACTION_FIELD_NAME]: `${transactionId}:create`, - }); - this.service.verbose( - `Validating nestedly created entities: ${model}#[${createdIds.join( - ', ' - )}]` - ); - await checkPolicyForIds( - model, - createdIds, - 'create', - this.service, - context, - tx - ); - }) - ); - } - ); - } catch (err) { - throw this.processError(err, 'update', model); - } - - // verify that return data requested by query args pass policy check - const readArgs = { ...args }; - delete readArgs.data; - - try { - const result = await readWithCheck( - model, - readArgs, - this.service, - context, - this.db - ); - if (result.length === 0) { - throw new CRUDError( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ); - } - return result[0]; - } catch (err) { - if ( - err instanceof CRUDError && - err.code === ServerErrorCode.DENIED_BY_POLICY - ) { - throw new CRUDError( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ); - } else { - throw err; - } - } - } - - async del( - model: string, - id: string, - args: any, - context: QueryContext - ): Promise { - let result: unknown; - - try { - // ensures the item under deletion passes policy check - await checkPolicyForIds( - model, - [id], - 'delete', - this.service, - context, - this.db - ); - - args = args ?? {}; - args.where = { ...args.where, id }; - - result = await this.db.$transaction( - async (tx: Record) => { - // first fetch the data that needs to be returned after deletion - let readResult: any; - try { - const items = await readWithCheck( - model, - args, - this.service, - context, - tx - ); - readResult = items[0]; - } catch (err) { - if ( - err instanceof CRUDError && - err.code === ServerErrorCode.DENIED_BY_POLICY - ) { - // can't read back, just return undefined, outer logic handles it - } else { - throw err; - } - } - - // conduct the deletion - this.service.verbose( - `Conducting delete ${model}:\n${superjson.stringify( - args - )}` - ); - await tx[model].delete(args); - - return readResult; - } - ); - } catch (err) { - throw this.processError(err, 'del', model); - } - - if (result) { - return result; - } else { - throw new CRUDError(ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED); - } - } - - private isPrismaClientKnownRequestError( - err: any - ): err is { code: string; message: string } { - return ( - err.__proto__.constructor.name === 'PrismaClientKnownRequestError' - ); - } - - private isPrismaClientValidationError( - err: any - ): err is { message: string } { - return err.__proto__.constructor.name === 'PrismaClientValidationError'; - } - - private processError( - err: unknown, - operation: 'get' | 'find' | 'create' | 'update' | 'del', - model: string - ) { - if (err instanceof CRUDError) { - return err; - } - - if (this.isPrismaClientKnownRequestError(err)) { - this.service.warn( - `Prisma request error: ${operation} ${model}: ${err}` - ); - - // errors thrown by Prisma, try mapping to a known error - if (PRISMA_ERROR_MAPPING[err.code]) { - return new CRUDError( - PRISMA_ERROR_MAPPING[err.code], - getServerErrorMessage(PRISMA_ERROR_MAPPING[err.code]) - ); - } else { - return new CRUDError( - ServerErrorCode.UNKNOWN, - 'an unhandled Prisma error occurred: ' + err.code - ); - } - } else if (this.isPrismaClientValidationError(err)) { - this.service.warn( - `Prisma validation error: ${operation} ${model}: ${err}` - ); - - // prisma validation error - return new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - getServerErrorMessage(ServerErrorCode.INVALID_REQUEST_PARAMS) - ); - } else if (err instanceof ValidationError) { - this.service.warn( - `Field constraint validation error: ${operation} ${model}: ${err.message}` - ); - - return new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - err.message - ); - } else { - // generic errors - this.service.error( - `An unknown error occurred: ${JSON.stringify(err)}` - ); - if (err instanceof Error && err.stack) { - this.service.error(err.stack); - } - return new CRUDError( - ServerErrorCode.UNKNOWN, - getServerErrorMessage(ServerErrorCode.UNKNOWN) - ); - } - } -} diff --git a/packages/runtime/src/handler/data/handler.ts b/packages/runtime/src/handler/data/handler.ts deleted file mode 100644 index 7c8c8caf9..000000000 --- a/packages/runtime/src/handler/data/handler.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; -import superjson from 'superjson'; -import { RequestHandlerOptions } from '../../request-handler'; -import { registerSerializers } from '../../serialization-utils'; -import { - DbClientContract, - QueryContext, - ServerErrorCode, - Service, -} from '../../types'; -import { CRUDError, RequestHandler } from '../types'; -import { CRUD } from './crud'; - -registerSerializers(); - -/** - * Request handler for /data endpoint which processes data CRUD requests. - */ -export default class DataHandler - implements RequestHandler -{ - private readonly crud: CRUD; - - constructor( - private readonly service: Service, - private readonly options: RequestHandlerOptions - ) { - this.crud = new CRUD(service); - } - - async handle( - req: NextApiRequest, - res: NextApiResponse, - path: string[] - ): Promise { - const [model, id] = path; - const method = req.method; - - const context = { user: await this.options.getServerUser(req, res) }; - - this.service.verbose(`Data request: ${method} ${path}`); - if (req.body) { - this.service.verbose( - `Request body: ${superjson.stringify(req.body)}` - ); - } - - try { - switch (method) { - case 'GET': - await this.get(req, res, model, id, context); - break; - - case 'POST': - await this.post(req, res, model, context); - break; - - case 'PUT': - await this.put(req, res, model, id, context); - break; - - case 'DELETE': - await this.del(req, res, model, id, context); - break; - - default: - this.service.warn(`Unhandled method: ${method}`); - res.status(200).send({}); - break; - } - } catch (err: unknown) { - if (err instanceof CRUDError) { - this.service.warn(`${method} ${model}: ${err}`); - - // in case of errors thrown directly by ZenStack - switch (err.code) { - case ServerErrorCode.DENIED_BY_POLICY: - case ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED: - res.status(403).send({ - code: err.code, - message: err.message, - }); - break; - - case ServerErrorCode.ENTITY_NOT_FOUND: - res.status(404).send({ - code: err.code, - message: err.message, - }); - break; - - case ServerErrorCode.UNKNOWN: - res.status(500).send({ - code: err.code, - message: err.message, - }); - break; - - default: - res.status(400).send({ - code: err.code, - message: err.message, - }); - } - } - } - } - - private marshal(value: unknown) { - return JSON.parse(superjson.stringify(value)); - } - - private unmarshal(value: unknown) { - if (typeof value === 'string') { - return superjson.parse(value); - } else { - return superjson.parse(JSON.stringify(value)); - } - } - - private async get( - req: NextApiRequest, - res: NextApiResponse, - model: string, - id: string, - context: QueryContext - ) { - // parse additional query args from "q" parameter - const args = req.query.q ? this.unmarshal(req.query.q as string) : {}; - - if (id) { - // GET /:id, make sure "id" is injected - const result = await this.crud.get(model, id, args, context); - if (!result) { - throw new CRUDError(ServerErrorCode.ENTITY_NOT_FOUND); - } - res.status(200).send(this.marshal(result)); - } else { - // GET /, get list - const result = await this.crud.find(model, args, context); - res.status(200).send(this.marshal(result)); - } - } - - private async post( - req: NextApiRequest, - res: NextApiResponse, - model: string, - context: QueryContext - ) { - const result = await this.crud.create( - model, - this.unmarshal(req.body), - context - ); - res.status(201).send(this.marshal(result)); - } - - private async put( - req: NextApiRequest, - res: NextApiResponse, - model: string, - id: string, - context: QueryContext - ) { - if (!id) { - throw new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - 'missing "id" parameter' - ); - } - - const result = await this.crud.update( - model, - id, - this.unmarshal(req.body), - context - ); - res.status(200).send(this.marshal(result)); - } - - private async del( - req: NextApiRequest, - res: NextApiResponse, - model: string, - id: string, - context: QueryContext - ) { - if (!id) { - throw new CRUDError( - ServerErrorCode.INVALID_REQUEST_PARAMS, - 'missing "id" parameter' - ); - } - - const args = req.query.q ? this.unmarshal(req.query.q as string) : {}; - const result = await this.crud.del(model, id, args, context); - res.status(200).send(this.marshal(result)); - } -} diff --git a/packages/runtime/src/handler/data/nested-write-vistor.ts b/packages/runtime/src/handler/data/nested-write-vistor.ts deleted file mode 100644 index 9446b8ec7..000000000 --- a/packages/runtime/src/handler/data/nested-write-vistor.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { FieldInfo, Service } from '../../types'; -import { PrismaWriteActions, PrismaWriteActionType } from '../types'; - -/** - * Visitor callback function type - * - * @fieldInfo current visiting field - * @action prisma action for this field, e.g., update, create, etc. - * @fieldData data attached to the field, a scalar type for simple field - * and nested structure for model field - * @parentData parent data of @see fieldData, can be used to replace current field data - * @state a custom state - * - * @return if a truethy value is returned, recursive visiting will continue and the return - * value will be used as the new state passed to visiting of the direct child level; otherwise - * visiting is stopped at this level - */ -export type NestedWriterVisitorCallback = ( - fieldInfo: FieldInfo, - action: PrismaWriteActionType, - fieldData: any, - parentData: any, - state: State -) => Promise; - -/** - * Recursive visitor for nested write (create/update) payload - */ -export class NestedWriteVisitor { - constructor(private readonly service: Service) {} - - private isPrismaWriteAction(value: string): value is PrismaWriteActionType { - return PrismaWriteActions.includes(value as PrismaWriteActionType); - } - - /** - * Start visiting - * - * @see NestedWriterVisitorCallback - */ - async visit( - model: string, - fieldData: any, - parentData: any, - state: State, - callback: NestedWriterVisitorCallback - ): Promise { - if (!fieldData) { - return; - } - - for (const [field, payload] of Object.entries(fieldData)) { - if (!payload) { - continue; - } - - const fieldInfo = await this.service.resolveField(model, field); - if (!fieldInfo) { - continue; - } - - if (!fieldInfo.isDataModel) { - // simple field, just call action - await callback(fieldInfo, 'none', payload, fieldData, state); - } else { - // deal with nested write of other model, here payload is a - // potentially nested structure like: - // - // { update: { field: {...} } } - // - for (const [subKey, subPayload] of Object.entries( - payload - )) { - if (this.isPrismaWriteAction(subKey) && subPayload) { - const newState = await callback( - fieldInfo, - subKey, - subPayload, - payload, - state - ); - if (newState) { - // recurse into content - await this.visit( - fieldInfo.type, - subPayload, - payload, - newState, - callback - ); - } - } - } - } - } - } -} diff --git a/packages/runtime/src/handler/data/policy-utils.ts b/packages/runtime/src/handler/data/policy-utils.ts deleted file mode 100644 index a982aeb2c..000000000 --- a/packages/runtime/src/handler/data/policy-utils.ts +++ /dev/null @@ -1,647 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { hashSync } from 'bcryptjs'; -import deepcopy from 'deepcopy'; -import superjson from 'superjson'; -import { - DEFAULT_PASSWORD_SALT_LENGTH, - GUARD_FIELD_NAME, - TRANSACTION_FIELD_NAME, -} from '../../constants'; -import { - DbOperations, - FieldInfo, - PolicyOperationKind, - QueryContext, - ServerErrorCode, - Service, -} from '../../types'; -import { CRUDError, PrismaWriteActionType } from '../types'; -import { NestedWriteVisitor } from './nested-write-vistor'; - -//#region General helpers - -/** - * Creates a conjunction of a list of query conditions. - */ -export function and(...conditions: unknown[]): any { - const filtered = conditions.filter((c) => !!c); - if (filtered.length === 0) { - return undefined; - } else if (filtered.length === 1) { - return filtered[0]; - } else { - return { AND: filtered }; - } -} - -/** - * Creates a disjunction of a list of query conditions. - */ -export function or(...conditions: unknown[]): any { - const filtered = conditions.filter((c) => !!c); - if (filtered.length === 0) { - return undefined; - } else if (filtered.length === 1) { - return filtered[0]; - } else { - return { OR: filtered }; - } -} - -/** - * Wraps a value into array if it's not already one - */ -export function ensureArray(value: T): Array { - return Array.isArray(value) ? value : [value]; -} - -/** - * Given a where condition, queries db and returns IDs of result entities - */ -export async function queryIds( - model: string, - db: Record, - where: unknown -): Promise { - const r = await db[model].findMany({ select: { id: true }, where }); - return (r as { id: string }[]).map((item) => item.id); -} - -//#endregion - -//#region Policy enforcement helpers - -/** - * Read model entities w.r.t the given query args. The result list - * are guaranteed to fully satisfy 'read' policy rules recursively. - * - * For to-many relations involved, items not satisfying policy are - * silently trimmed. For to-one relation, if relation data fails policy - * an CRUDError is thrown. - * - * @param model the model to query for - * @param queryArgs the Prisma query args - * @param service the ZenStack service - * @param context the query context - * @param db the db (or transaction) - * @returns - */ -export async function readWithCheck( - model: string, - queryArgs: any, - service: Service, - context: QueryContext, - db: Record -): Promise { - const args = deepcopy(queryArgs); - args.where = and( - args.where, - await service.buildQueryGuard(model, 'read', context) - ); - - // recursively inject read guard conditions into the query args - await injectNestedReadConditions(model, args, service, context); - - service.verbose( - `Reading with validation for ${model}: ${superjson.stringify(args)}` - ); - const result = await db[model].findMany(args); - - await Promise.all( - result.map((item) => - postProcessForRead(item, model, args, service, context, db, 'read') - ) - ); - - return result; -} - -async function injectNestedReadConditions( - model: string, - args: any, - service: Service, - context: QueryContext -) { - const injectTarget = args.select ?? args.include; - if (!injectTarget) { - return; - } - - for (const field of Object.keys(injectTarget)) { - const fieldInfo = await service.resolveField(model, field); - if (!fieldInfo || !fieldInfo.isDataModel) { - // only care about relation fields - continue; - } - - if (fieldInfo.isArray) { - if (typeof injectTarget[field] !== 'object') { - injectTarget[field] = {}; - } - // inject extra condition for to-many relation - injectTarget[field].where = and( - injectTarget.where, - await service.buildQueryGuard(fieldInfo.type, 'read', context) - ); - } else { - // there's no way of injecting condition for to-one relation, so we - // make sure 'id' field is selected and check them against query result - if ( - injectTarget[field]?.select && - injectTarget[field]?.select?.id !== true - ) { - injectTarget[field].select.id = true; - } - } - - // recurse - await injectNestedReadConditions( - fieldInfo.type, - injectTarget[field], - service, - context - ); - } -} - -/** - * Post processing checks for read model entities. - * Validates to-one relations (which can't be trimmed - * at query time) and removes fields that should be - * omitted. - */ -async function postProcessForRead( - entityData: any, - model: string, - args: any, - service: Service, - context: QueryContext, - db: Record, - operation: PolicyOperationKind -) { - if (!entityData?.id) { - return; - } - - for (const field of Object.keys(entityData)) { - if (await shouldOmit(service, model, field)) { - delete entityData[field]; - } - } - - const injectTarget = args.select ?? args.include; - if (!injectTarget) { - return; - } - - // to-one relation data cannot be trimmed by injected guards, we have to - // post-check them - for (const field of Object.keys(injectTarget)) { - const fieldInfo = await service.resolveField(model, field); - if ( - !fieldInfo || - !fieldInfo.isDataModel || - fieldInfo.isArray || - !entityData?.[field]?.id - ) { - continue; - } - - service.verbose( - `Validating read of to-one relation: ${fieldInfo.type}#${entityData[field].id}` - ); - - await checkPolicyForIds( - fieldInfo.type, - [entityData[field].id], - operation, - service, - context, - db - ); - - // recurse - await postProcessForRead( - entityData[field], - fieldInfo.type, - injectTarget[field], - service, - context, - db, - operation - ); - } -} - -type SelectionPath = Array<{ field: FieldInfo; where: any }>; - -/** - * Validates that a model entity satisfies 'update' policy rules - * before conducting an update - * - * @param model model under update - * @param id id of entity under update - * @param updateArgs Prisma update args - * @param service the ZenStack service - * @param context the query context - * @param transaction the db transaction context - */ -export async function preUpdateCheck( - model: string, - id: string, - updateArgs: any, - service: Service, - context: QueryContext, - transaction: Record -): Promise { - // check the entity directly under update first - await checkPolicyForIds( - model, - [id], - 'update', - service, - context, - transaction - ); - - // We need to ensure that all nested updates respect policy rules of - // the corresponding model. - // - // Here we use a visitor to collect all necessary - // checkes. During visiting, for every update we meet agains a relation, - // we collect its path (starting from the root object). If the relation - // is a to-many one, it can carry filter condition that we collect as well. - // - // After the visiting, we validate that each collected path satisfies - // corresponding policy rules by making separate queries. - - const visitor = new NestedWriteVisitor(service); - const state: SelectionPath = []; - const checks: Array> = []; - - const visitAction = async ( - fieldInfo: FieldInfo, - action: PrismaWriteActionType, - fieldData: any, - _parentData: any, - state: SelectionPath - ) => { - if (!fieldInfo.isDataModel) { - return state; - } - - if ( - ![ - 'update', - 'updateMany', - 'upsert', - 'delete', - 'deleteMany', - ].includes(action) - ) { - // no more nested writes inside, stop recursion - return undefined; - } - - // for to-many relation, a filter condition can be attached - let condition: any = undefined; - if (fieldInfo.isArray) { - switch (action) { - case 'update': - case 'updateMany': - case 'upsert': - // condition is wrapped in 'where' - condition = or( - ...ensureArray(fieldData).map((d) => d.where) - ); - break; - case 'delete': - case 'deleteMany': - // condition is not wrapped - condition = or(...ensureArray(fieldData)); - break; - } - } - - // build up a new segment of path - const selectionPath = [ - ...state, - { field: fieldInfo, where: condition }, - ]; - - const operation: PolicyOperationKind = [ - 'update', - 'updateMany', - 'upsert', - ].includes(action) - ? 'update' - : 'delete'; - - // collect an asynchronous check action - checks.push( - checkPolicyForSelectionPath( - model, - id, - selectionPath, - operation, - transaction, - service, - context - ) - ); - - // recurse down with the current path as the new state - return selectionPath; - }; - - await visitor.visit(model, updateArgs.data, undefined, state, visitAction); - - await Promise.all(checks); -} - -/** - * Given a list of ids for a model, check if they all match policy rules, and if not, - * throw a CRUDError. - * - * @param model the model - * @param ids the entity ids - * @param operation the operation to check for - * @param service the ZenStack service - * @param context the query context - * @param db the db or transaction - */ -export async function checkPolicyForIds( - model: string, - ids: string[], - operation: PolicyOperationKind, - service: Service, - context: QueryContext, - db: Record -) { - service.verbose( - `Checking policy for ${model}#[${ids.join(', ')}] for ${operation}` - ); - - // build a query condition with policy injected - const idCondition = ids.length > 1 ? { id: { in: ids } } : { id: ids[0] }; - const query = { - where: and( - idCondition, - await service.buildQueryGuard(model, operation, context) - ), - select: { id: true }, - }; - - // query with policy injected - const filteredResult = (await db[model].findMany(query)) as Array<{ - id: string; - }>; - - // see if we get fewer items with policy, if so, reject with an throw - const filteredIds = filteredResult.map((item) => item.id); - if (filteredIds.length < ids.length) { - const gap = ids.filter((id) => !filteredIds.includes(id)); - throw new CRUDError( - ServerErrorCode.DENIED_BY_POLICY, - `denied by policy: entities failed '${operation}' check, ${model}#[${gap.join( - ', ' - )}]` - ); - } -} - -/** - * Given a selection path, check if the entities at the end of path satisfy - * policy rules. If not, throw an error. - */ -async function checkPolicyForSelectionPath( - model: string, - id: string, - selectionPath: SelectionPath, - operation: PolicyOperationKind, - db: Record, - service: Service, - context: QueryContext -): Promise { - const targetField = selectionPath[selectionPath.length - 1].field; - // build a Prisma query for the path - const query = buildChainedSelectQuery(id, selectionPath); - - service.verbose( - `Query for selection path: model ${model}, path ${superjson.stringify( - selectionPath - )}, query ${superjson.stringify(query)}` - ); - const r = await db[model].findUnique(query); - - // collect ids at the end of the path - const ids: string[] = collectTerminalEntityIds(selectionPath, r); - service.verbose(`Collected leaf ids: ${superjson.stringify(ids)}`); - - if (ids.length === 0) { - return; - } - - // check policies for the collected ids - await checkPolicyForIds( - targetField.type, - ids, - operation, - service, - context, - db - ); -} - -/** - * Builds a Prisma query for the given selection path - */ -function buildChainedSelectQuery(id: string, selectionPath: SelectionPath) { - const query = { where: { id }, select: { id: true } }; - let currSelect: any = query.select; - for (const path of selectionPath) { - const nextSelect = { select: { id: true } }; - currSelect[path.field.name] = nextSelect; - if (path.where) { - currSelect[path.field.name].where = path.where; - } - currSelect = nextSelect.select; - } - return query; -} - -function collectTerminalEntityIds( - selectionPath: SelectionPath, - data: any -): string[] { - let curr = data; - for (const path of selectionPath) { - curr = curr[path.field.name]; - } - - if (!curr) { - throw new CRUDError( - ServerErrorCode.UNKNOWN, - 'an unexpected error occurred' - ); - } - - return Array.isArray(curr) - ? curr.map((item) => item.id as string) - : [curr.id as string]; -} - -/** - * Injects assignment of zenstack_transaction field for all nested - * update/create in a Prisma update args recursively. - * - * @return a tuple containing all model types that are involved in - * creation or updating, respectively - */ -export async function injectTransactionId( - model: string, - args: any, - operation: PolicyOperationKind, - transactionId: string, - service: Service -): Promise<{ createdModels: string[]; updatedModels: string[] }> { - const updatedModels = new Set(); - const createdModels = new Set(); - - if (args.data) { - args.data[TRANSACTION_FIELD_NAME] = `${transactionId}:${operation}`; - updatedModels.add(model); - } - - const visitAction = async ( - fieldInfo: FieldInfo, - action: PrismaWriteActionType, - fieldData: any - ) => { - if (fieldInfo.isDataModel && fieldData) { - switch (action) { - case 'update': - case 'updateMany': - ensureArray(fieldData).forEach((item) => { - if (fieldInfo.isArray && item.data) { - item.data[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:update`; - } else { - item[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:update`; - } - updatedModels.add(fieldInfo.type); - }); - break; - - case 'upsert': - ensureArray(fieldData).forEach((item) => { - item.create[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:create`; - createdModels.add(fieldInfo.type); - item.update[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:update`; - updatedModels.add(fieldInfo.type); - }); - break; - - case 'create': - case 'createMany': - ensureArray(fieldData).forEach((item) => { - item[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:create`; - createdModels.add(fieldInfo.type); - }); - break; - - case 'connectOrCreate': - ensureArray(fieldData).forEach((item) => { - item.create[ - TRANSACTION_FIELD_NAME - ] = `${transactionId}:create`; - createdModels.add(fieldInfo.type); - }); - break; - } - } - return true; - }; - - const visitor = new NestedWriteVisitor(service); - await visitor.visit(model, args.data, undefined, undefined, visitAction); - - return { - createdModels: Array.from(createdModels), - updatedModels: Array.from(updatedModels), - }; -} - -/** - * Preprocesses the given write args to modify field values (in place) based on - * attributes like @password - */ -export async function preprocessWritePayload( - model: string, - args: any, - service: Service -) { - const visitAction = async ( - fieldInfo: FieldInfo, - _action: PrismaWriteActionType, - fieldData: any, - parentData: any - ) => { - // process @password field - const pwdAttr = fieldInfo.attributes?.find( - (attr) => attr.name === '@password' - ); - if (pwdAttr && fieldInfo.type === 'String') { - // hash password value - let salt: string | number | undefined = pwdAttr.args.find( - (arg) => arg.name === 'salt' - )?.value as string; - if (!salt) { - salt = pwdAttr.args.find((arg) => arg.name === 'saltLength') - ?.value as number; - } - if (!salt) { - salt = DEFAULT_PASSWORD_SALT_LENGTH; - } - parentData[fieldInfo.name] = hashSync(fieldData, salt); - } - - // deserialize Buffer field - if (fieldInfo.type === 'Bytes' && Array.isArray(fieldData.data)) { - parentData[fieldInfo.name] = Buffer.from(fieldData.data); - } - - // deserialize BigInt field - if (fieldInfo.type === 'BigInt' && typeof fieldData === 'string') { - parentData[fieldInfo.name] = BigInt(fieldData); - } - - return true; - }; - - const visitor = new NestedWriteVisitor(service); - - await visitor.visit(model, args.data, undefined, undefined, visitAction); -} - -async function shouldOmit(service: Service, model: string, field: string) { - if ([TRANSACTION_FIELD_NAME, GUARD_FIELD_NAME].includes(field)) { - return true; - } - const fieldInfo = await service.resolveField(model, field); - return !!( - fieldInfo && fieldInfo.attributes.find((attr) => attr.name === '@omit') - ); -} - -//#endregion diff --git a/packages/runtime/src/handler/index.ts b/packages/runtime/src/handler/index.ts deleted file mode 100644 index a73009277..000000000 --- a/packages/runtime/src/handler/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DataHandler } from './data/handler'; diff --git a/packages/runtime/src/handler/types.ts b/packages/runtime/src/handler/types.ts deleted file mode 100644 index 54701690d..000000000 --- a/packages/runtime/src/handler/types.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; -import { getServerErrorMessage, ServerErrorCode } from '../types'; - -/** - * Defines contract for a Next.js API endpoint handler. - */ -export interface RequestHandler { - /** - * Handles a request for a given route path. - * - * @param req The request - * @param res The response - * @param path The route path (with /api/zenstack prefix removed) - */ - handle( - req: NextApiRequest, - res: NextApiResponse, - path: string[] - ): Promise; -} - -/** - * Error thrown during CRUD operations - */ -export class CRUDError extends Error { - constructor(public readonly code: ServerErrorCode, message?: string) { - message = message - ? `${getServerErrorMessage(code)}: ${message}` - : getServerErrorMessage(code); - super(message); - } - - toString(): string { - return `Request handler error: ${this.code}, ${this.message}`; - } -} - -/** - * All write actions supported by Prisma - */ -export const PrismaWriteActions = [ - 'create', - 'createMany', - 'connectOrCreate', - 'update', - 'updateMany', - 'upsert', - 'delete', - 'deleteMany', - 'connect', - 'none', -] as const; - -export type PrismaWriteActionType = typeof PrismaWriteActions[number]; diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 3eb8ed7b8..a3554d2d5 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,5 +1,5 @@ +export * from './enhancements'; +export * from './enhancements/policy'; export * from './types'; -export * from './config'; -export * from './service'; -export * from './request-handler'; export * from './validation'; +export * from './error'; diff --git a/packages/runtime/src/request-handler.ts b/packages/runtime/src/request-handler.ts deleted file mode 100644 index d182e6557..000000000 --- a/packages/runtime/src/request-handler.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; -import { DataHandler } from './handler'; -import { AuthUser, DbClientContract, Service } from './types'; - -/** - * Options for initializing a Next.js API endpoint request handler. This type is re-exported in @zenstackhq/runtime/server. - * @see requestHandler - */ -export type RequestHandlerOptions = { - /** - * Hook method for providing current login user from session. - */ - getServerUser: ( - req: NextApiRequest, - res: NextApiResponse - ) => Promise; -}; - -/** - * Creates a Next.js API endpoint request handler which encapsulates RESTful APIs generated by ZenStack. - * The created handler should be mounted at /api/zenstack endpoint. - * - * @param service ZenStack service which wraps a Prisma db client inside - * @param options Options for initialization - * @returns An API endpoint request handler - */ -export function requestHandler( - service: Service, - options: RequestHandlerOptions -): (req: NextApiRequest, res: NextApiResponse) => Promise { - const dataHandler = new DataHandler( - service as Service, - options - ); - return async (req: NextApiRequest, res: NextApiResponse) => { - const [route, ...rest] = req.query.path as string[]; - switch (route) { - // "/data" route is for handling data-access requests - case 'data': - return dataHandler.handle(req, res, rest); - - default: - service.warn(`Unknown route: ${route}`); - res.status(404).json({ error: `Unknown route: ${route}` }); - } - }; -} diff --git a/packages/runtime/src/service.ts b/packages/runtime/src/service.ts deleted file mode 100644 index e240f522f..000000000 --- a/packages/runtime/src/service.ts +++ /dev/null @@ -1,201 +0,0 @@ -import colors from 'colors'; -import * as fs from 'fs'; -import { EventEmitter } from 'stream'; -import { z } from 'zod'; -import { ServiceConfig } from './config'; -import { - FieldInfo, - LogEvent, - LogLevel, - PolicyOperationKind, - QueryContext, - Service, -} from './types'; -import { validate } from './validation'; - -export abstract class DefaultService< - DbClient extends { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - $on: (eventType: any, handler: (event: any) => void) => void; - } -> implements Service -{ - protected config: ServiceConfig; - private prisma: DbClient; - protected readonly logEmitter = new EventEmitter(); - private readonly logSettings = { - query: { stdout: false, emit: false }, - verbose: { stdout: false, emit: false }, - info: { stdout: true, emit: false }, - warn: { stdout: true, emit: false }, - error: { stdout: true, emit: false }, - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private guardModule: any; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private fieldConstraintModule: any; - - private readonly prismaLogLevels: LogLevel[] = [ - 'query', - 'info', - 'warn', - 'error', - ]; - - constructor() { - this.initialize(); - } - - private initialize() { - this.config = this.loadConfig(); - - // initialize log sink mapping - if (this.config.log) { - // reset all levels - for (const key of Object.keys(this.logSettings)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this.logSettings as any)[key] = { stdout: false, emit: false }; - } - - for (const entry of this.config.log) { - const level = typeof entry === 'string' ? entry : entry.level; - if (!Object.keys(this.logSettings).includes(level)) { - console.error(`Unknown log level "${level}"`); - continue; - } - if (typeof entry === 'string') { - this.logSettings[level].stdout = true; - } else if (entry.emit === 'stdout') { - this.logSettings[level].stdout = true; - } else { - this.logSettings[level].emit = true; - } - } - } - - this.prisma = this.initializePrisma(); - - for (const level of this.prismaLogLevels) { - if (this.logSettings[level].emit) { - this.verbose(`Hooking prisma log level ${level}`); - this.prisma.$on(level, (e) => { - this.logEmitter.emit(level, e); - }); - } - } - } - - $on(level: LogLevel, callback: (event: LogEvent) => void): void { - this.logEmitter.on(level, callback); - } - - private handleLog(level: LogLevel, message: string): void { - if (this.logSettings[level].stdout) { - switch (level) { - case 'verbose': - console.log(colors.blue(`zenstack:${level}`), message); - break; - case 'info': - console.log(colors.cyan(`zenstack:${level}`), message); - break; - case 'warn': - console.warn(colors.yellow(`zenstack:${level}`), message); - break; - case 'error': - console.error(colors.red(`zenstack:${level}`), message); - break; - } - } - if (this.logSettings[level].emit) { - this.logEmitter.emit(level, { timestamp: new Date(), message }); - } - } - - private loadConfig(): ServiceConfig { - const configFile = './zenstack.config.json'; - if (fs.existsSync(configFile)) { - try { - const config = JSON.parse( - fs.readFileSync(configFile).toString('utf-8') - ); - return config as ServiceConfig; - } catch (err) { - console.error('Failed to load zenstack.config.json', err); - } - } - return {}; - } - - get db(): DbClient { - return this.prisma; - } - - async resolveField( - model: string, - field: string - ): Promise { - if (!this.guardModule) { - this.guardModule = await this.loadGuardModule(); - } - return this.guardModule._fieldMapping?.[model]?.[field]; - } - - async buildQueryGuard( - model: string, - operation: PolicyOperationKind, - context: QueryContext - ): Promise { - if (!this.guardModule) { - this.guardModule = await this.loadGuardModule(); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const provider: (context: QueryContext) => any = - this.guardModule[model + '_' + operation]; - return provider(context); - } - - async validateModelPayload( - model: string, - mode: 'create' | 'update', - payload: unknown - ) { - if (!this.fieldConstraintModule) { - this.fieldConstraintModule = await this.loadFieldConstraintModule(); - } - const validator = this.fieldConstraintModule[ - `${model}_${mode}_validator` - ] as z.ZodType; - if (validator) { - validate(validator, payload); - } - } - - verbose(message: string): void { - this.handleLog('verbose', message); - } - - info(message: string): void { - this.handleLog('info', message); - } - - warn(message: string): void { - this.handleLog('warn', message); - } - - error(message: string): void { - this.handleLog('error', message); - } - - reinitialize(): void { - this.initialize(); - } - - protected abstract initializePrisma(): DbClient; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - protected abstract loadGuardModule(): Promise; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - protected abstract loadFieldConstraintModule(): Promise; -} diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index 4b8f85508..39b3c4248 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -2,14 +2,21 @@ * Weakly-typed database access methods */ export interface DbOperations { - findMany(args: unknown): Promise; + findMany(args?: unknown): Promise; findFirst(args: unknown): Promise; + findFirstOrThrow(args: unknown): Promise; findUnique(args: unknown): Promise; + findUniqueOrThrow(args: unknown): Promise; create(args: unknown): Promise; + createMany(args: unknown, skipDuplicates?: boolean): Promise<{ count: number }>; update(args: unknown): Promise; + updateMany(args: unknown): Promise<{ count: number }>; + upsert(args: unknown): Promise; delete(args: unknown): Promise; - deleteMany(args: unknown): Promise; - count(args: unknown): Promise; + deleteMany(args?: unknown): Promise<{ count: number }>; + aggregate(args: unknown): Promise; + groupBy(args: unknown): Promise; + count(args?: unknown): Promise; } /** @@ -20,14 +27,12 @@ export type PolicyKind = 'allow' | 'deny'; /** * Kinds of operations controlled by access policies */ -export type PolicyOperationKind = 'create' | 'update' | 'read' | 'delete'; +export type PolicyOperationKind = 'create' | 'update' | 'postUpdate' | 'read' | 'delete'; /** * Current login user info - * - * @todo Support for non-string "id" field */ -export type AuthUser = { id: string } & Record; +export type AuthUser = Record; /** * Context for database query @@ -37,6 +42,9 @@ export type QueryContext = { * Current login user (provided by @see RequestHandlerOptions) */ user?: AuthUser; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + preValue?: any; }; export type RuntimeAttribute = { @@ -50,195 +58,27 @@ export type RuntimeAttribute = { export type FieldInfo = { name: string; type: string; + isId: boolean; isDataModel: boolean; isArray: boolean; isOptional: boolean; attributes: RuntimeAttribute[]; + backLink?: string; }; export type DbClientContract = Record & { - $transaction: ( - action: (tx: Record) => Promise - ) => Promise; + $transaction: (action: (tx: Record) => Promise) => Promise; }; -/** - * Logging levels - */ -export type LogLevel = 'verbose' | 'info' | 'query' | 'warn' | 'error'; - -/** - * The main service of ZenStack. Implementation of this interface is automatically generated. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface Service { - /** - * Returns the wrapped Prisma db client - */ - get db(): DbClient; - - /** - * Resolves information of a data model field. - * - * @param model Model name - * @param field Field name - */ - resolveField(model: string, field: string): Promise; - - /** - * Builds policy check guard object for an operation over a model, which will be injected to - * the query body sent to Prisma client. - * - * @param model Model name - * @param operation Operation kind - * @param context Query context - */ - buildQueryGuard( - model: string, - operation: PolicyOperationKind, - context: QueryContext - ): Promise; - - /** - * Validates the given write payload for the given model according to field constraints in model. - * - * @param model Model name - * @param mode Write mode - * @param payload Write payload - * - * @throws @see ValidationError - */ - validateModelPayload( - model: string, - mode: 'create' | 'update', - payload: unknown - ): Promise; - - /** - * Generates a log message with verbose level. - */ - verbose(message: string): void; - - /** - * Generates a log message with info level. - */ - info(message: string): void; - - /** - * Generates a log message with warn level. - */ - warn(message: string): void; - - /** - * Generates a log message with error level. - */ - error(message: string): void; - - /** - * Registers a listener to log events. - */ - $on(level: LogLevel, callback: (event: LogEvent) => void): void; -} - -/** - * Error codes for errors on server side - */ -export enum ServerErrorCode { - /** - * The specified entity cannot be found - */ - ENTITY_NOT_FOUND = 'ENTITY_NOT_FOUND', - - /** - * The request parameter is invalid, either containing invalid fields or missing required fields - */ - INVALID_REQUEST_PARAMS = 'INVALID_REQUEST_PARAMS', - - /** - * The request is rejected by policy checks - */ - DENIED_BY_POLICY = 'DENIED_BY_POLICY', - - /** - * Violation of database unique constraints - */ - UNIQUE_CONSTRAINT_VIOLATION = 'UNIQUE_CONSTRAINT_VIOLATION', - - /** - * Violation of database reference constraint (aka. foreign key constraints) - */ - REFERENCE_CONSTRAINT_VIOLATION = 'REFERENCE_CONSTRAINT_VIOLATION', - - /** - * A write operation succeeded but the result cannot be read back due to policy control - */ - READ_BACK_AFTER_WRITE_DENIED = 'READ_BACK_AFTER_WRITE_DENIED', - - /** - * Unknown error - */ - UNKNOWN = 'UNKNOWN', -} - -export function getServerErrorMessage(code: ServerErrorCode): string { - switch (code) { - case ServerErrorCode.ENTITY_NOT_FOUND: - return 'the requested entity is not found'; - - case ServerErrorCode.INVALID_REQUEST_PARAMS: - return 'request parameters are invalid'; - - case ServerErrorCode.DENIED_BY_POLICY: - return 'the request was denied due to access policy violation'; - - case ServerErrorCode.UNIQUE_CONSTRAINT_VIOLATION: - return 'the request failed because of database unique constraint violation'; - - case ServerErrorCode.REFERENCE_CONSTRAINT_VIOLATION: - return 'the request failed because of database foreign key constraint violation'; - - case ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED: - return 'the write operation succeeded, but the data cannot be read back due to access policy violation'; - - case ServerErrorCode.UNKNOWN: - return 'an unknown error occurred'; - - default: - return `generic error: ${code}`; - } -} - -export type LogEventHandler = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - LogEvent: any, - handler: (event: LogEvent) => void -) => void; - -export type LogEvent = { - timestamp: Date; - query?: string; - params?: string; - duration?: number; - target?: string; - message?: string; -}; - -/** - * Client request options - */ -export type RequestOptions = { - // disable data fetching - disabled?: boolean; - initialData?: T; -}; - -/** - * Hooks invocation error - */ -export type HooksError = { - status: number; - info: { - code: ServerErrorCode; - message: string; - }; -}; +export const PrismaWriteActions = [ + 'create', + 'createMany', + 'connectOrCreate', + 'update', + 'updateMany', + 'upsert', + 'delete', + 'deleteMany', +] as const; + +export type PrismaWriteActionType = typeof PrismaWriteActions[number]; diff --git a/packages/runtime/src/version.ts b/packages/runtime/src/version.ts new file mode 100644 index 000000000..200cae236 --- /dev/null +++ b/packages/runtime/src/version.ts @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +export function getVersion() { + try { + return require('./package.json').version; + } catch { + return require('../package.json').version; + } +} diff --git a/packages/runtime/tsconfig.json b/packages/runtime/tsconfig.json index a8215c9f4..f6dff1942 100644 --- a/packages/runtime/tsconfig.json +++ b/packages/runtime/tsconfig.json @@ -4,7 +4,7 @@ "module": "CommonJS", "lib": ["ESNext", "DOM"], "sourceMap": true, - "outDir": "dist/lib", + "outDir": "dist", "strict": true, "noUnusedLocals": true, "noImplicitReturns": true, diff --git a/packages/schema/.env b/packages/schema/.env deleted file mode 100644 index f2199d46b..000000000 --- a/packages/schema/.env +++ /dev/null @@ -1 +0,0 @@ -TELEMETRY_TRACKING_TOKEN= diff --git a/packages/schema/.eslintrc.json b/packages/schema/.eslintrc.json index d43cb4629..b34c9c23b 100644 --- a/packages/schema/.eslintrc.json +++ b/packages/schema/.eslintrc.json @@ -9,7 +9,11 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" + "plugin:@typescript-eslint/recommended", + "plugin:jest/recommended" ], + "rules": { + "jest/expect-expect": "off" + }, "ignorePatterns": ["src/language-server/generated/*"] } diff --git a/packages/schema/.gitignore b/packages/schema/.gitignore index 5d5b5d0c9..6bb80bf51 100644 --- a/packages/schema/.gitignore +++ b/packages/schema/.gitignore @@ -2,5 +2,5 @@ /tests/coverage/ /bundle *.vsix -/README.md /bin/post-install.js +.env diff --git a/packages/schema/LICENSE b/packages/schema/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/packages/schema/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/schema/LICENSE.md b/packages/schema/LICENSE.md deleted file mode 100644 index 91d4584d3..000000000 --- a/packages/schema/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 ZenStack - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/schema/README-global.md b/packages/schema/README-global.md new file mode 120000 index 000000000..fe8400541 --- /dev/null +++ b/packages/schema/README-global.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/packages/schema/README.md b/packages/schema/README.md new file mode 100644 index 000000000..ece352081 --- /dev/null +++ b/packages/schema/README.md @@ -0,0 +1,30 @@ +# ZenStack VS Code Extension + +[ZenStack](https://zenstack.dev) is a toolkit that simplifies the development of a web app's backend. It supercharges [Prisma ORM](https://prisma.io) with a powerful access control layer and unleashes its full potential for web development. + +This VS Code extension provides code editing helpers for authoring ZenStack's schema files (.zmodel files). + +## Features + +- Syntax highlighting +- Auto formatting +- Inline error reporting +- Go-to definition +- Hover documentation +- Code section folding + +## Links + +- [Home](https://zenstack.dev) +- [Documentation](https://zenstack.dev/docs) +- [Community chat](https://go.zenstack.dev/chat) +- [Twitter](https://twitter.com/zenstackhq) +- [Blog](https://dev.to/zenstack) + +## Community + +Join our [discord server](https://go.zenstack.dev/chat) for chat and updates! + +## License + +[MIT](https://github.com/zenstackhq/zenstack/blob/main/LICENSE) diff --git a/packages/schema/bin/cli b/packages/schema/bin/cli index 539efc156..1bd15e4f6 100755 --- a/packages/schema/bin/cli +++ b/packages/schema/bin/cli @@ -1,3 +1,3 @@ #!/usr/bin/env node -require('../bundle/cli').default(); +require('../cli').default(); diff --git a/packages/schema/bin/post-install.js b/packages/schema/bin/post-install.js index e69de29bb..202c26bf1 100644 --- a/packages/schema/bin/post-install.js +++ b/packages/schema/bin/post-install.js @@ -0,0 +1,24 @@ +try { + if (process.env.DO_NOT_TRACK == '1') { + process.exit(0); + } + + const Mixpanel = require('mixpanel'); + const machineId = require('node-machine-id'); + const os = require('os'); + + const mixpanel = Mixpanel.init('', { + geolocate: true, + }); + + const version = require('../package.json').version; + const payload = { + distinct_id: machineId.machineIdSync(), + nodeVersion: process.version, + time: new Date(), + $os: os.platform(), + version, + }; + + mixpanel.track('npm:install', payload); +} catch {} diff --git a/packages/schema/build/bundle.js b/packages/schema/build/bundle.js index 42c657982..fb683cb6d 100644 --- a/packages/schema/build/bundle.js +++ b/packages/schema/build/bundle.js @@ -2,15 +2,10 @@ const watch = process.argv.includes('--watch'); const minify = process.argv.includes('--minify'); const success = watch ? 'Watch build succeeded' : 'Build succeeded'; const fs = require('fs'); -const envFilePlugin = require('./env-plugin'); require('esbuild') .build({ - entryPoints: [ - 'src/extension.ts', - 'src/language-server/main.ts', - 'src/cli/index.ts', - ], + entryPoints: ['src/extension.ts', 'src/language-server/main.ts'], outdir: 'bundle', bundle: true, external: ['vscode'], @@ -25,7 +20,6 @@ require('esbuild') } : false, minify, - plugins: [envFilePlugin], }) .then(() => { fs.cpSync('./src/res', 'bundle/res', { force: true, recursive: true }); @@ -33,26 +27,15 @@ require('esbuild') force: true, recursive: true, }); - - require('dotenv').config(); - - if (process.env.TELEMETRY_TRACKING_TOKEN) { - let postInstallContent = fs.readFileSync( - 'script/post-install.js', - 'utf-8' - ); - postInstallContent = postInstallContent.replace( - '', - process.env.TELEMETRY_TRACKING_TOKEN - ); - fs.writeFileSync('bin/post-install.js', postInstallContent, { - encoding: 'utf-8', - }); - } else { - fs.writeFileSync('bin/post-install.js', '', { - encoding: 'utf-8', - }); - } + fs.cpSync('./README.md', 'bundle/README.md', { + force: true, + }); + fs.cpSync('../../LICENSE', 'bundle/LICENSE', { + force: true, + }); + fs.cpSync('./package.json', 'bundle/package.json', { + force: true, + }); }) .then(() => console.log(success)) .catch((err) => { diff --git a/packages/schema/build/env-plugin.js b/packages/schema/build/env-plugin.js index bb4e2c173..99e212616 100644 --- a/packages/schema/build/env-plugin.js +++ b/packages/schema/build/env-plugin.js @@ -12,12 +12,7 @@ module.exports = { function _findEnvFile(dir) { if (!fs.existsSync(dir)) return undefined; - const candidates = [ - `${dir}/.env.${ENV}.local`, - `${dir}/.env.${ENV}`, - `${dir}/.env.local`, - `${dir}/.env`, - ]; + const candidates = [`${dir}/.env.${ENV}.local`, `${dir}/.env.${ENV}`, `${dir}/.env.local`, `${dir}/.env`]; for (const candidate of candidates) { if (fs.existsSync(candidate)) { @@ -51,10 +46,7 @@ module.exports = { // read in .env file contents and combine with regular .env: let config = {}; if (args.pluginData && args.pluginData.envPath) { - let data = await fs.promises.readFile( - args.pluginData.envPath, - 'utf8' - ); + let data = await fs.promises.readFile(args.pluginData.envPath, 'utf8'); const buf = Buffer.from(data); config = require('dotenv').parse(buf); } diff --git a/packages/schema/build/post-build.js b/packages/schema/build/post-build.js new file mode 100644 index 000000000..c8623f4c5 --- /dev/null +++ b/packages/schema/build/post-build.js @@ -0,0 +1,29 @@ +require('dotenv').config({ path: './.env.local' }); +require('dotenv').config({ path: './.env' }); +const fs = require('fs'); + +const filesToReplace = ['dist/bin/post-install.js', 'dist/constants.js']; +for (const file of filesToReplace) { + let content = fs.readFileSync(file, 'utf-8'); + if (process.env.TELEMETRY_TRACKING_TOKEN) { + content = content.replace('', process.env.TELEMETRY_TRACKING_TOKEN); + } else { + content = content.replace('', ''); + } + console.log('Updating file:', file); + fs.writeFileSync(file, content, { + encoding: 'utf-8', + }); +} + +let cliContent = fs.readFileSync('dist/cli/index.js', 'utf-8'); +if (process.env.DEFAULT_NPM_TAG) { + cliContent = cliContent.replace('', process.env.DEFAULT_NPM_TAG); +} else { + cliContent = cliContent.replace('', 'latest'); +} + +console.log('Updating file: dist/cli/index.js'); +fs.writeFileSync('dist/cli/index.js', cliContent, { + encoding: 'utf-8', +}); diff --git a/packages/schema/jest.config.ts b/packages/schema/jest.config.ts index 7f4680801..917cf52f6 100644 --- a/packages/schema/jest.config.ts +++ b/packages/schema/jest.config.ts @@ -3,9 +3,6 @@ * https://jestjs.io/docs/configuration */ -import tsconfig from './tsconfig.json'; -const moduleNameMapper = require('tsconfig-paths-jest')(tsconfig); - export default { // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, @@ -29,6 +26,4 @@ export default { transform: { '^.+\\.tsx?$': 'ts-jest' }, testTimeout: 300000, - - moduleNameMapper, }; diff --git a/packages/schema/language-configuration.json b/packages/schema/language-configuration.json index 8f162a0c4..ea1cc2473 100644 --- a/packages/schema/language-configuration.json +++ b/packages/schema/language-configuration.json @@ -3,7 +3,7 @@ // symbol used for single line comment. Remove this entry if your language does not support line comments "lineComment": "//", // symbols used for start and end a block comment. Remove this entry if your language does not support block comments - "blockComment": [ "/*", "*/" ] + "blockComment": ["/*", "*/"] }, // symbols used as brackets "brackets": [ @@ -27,4 +27,4 @@ ["\"", "\""], ["'", "'"] ] -} \ No newline at end of file +} diff --git a/packages/schema/package.json b/packages/schema/package.json index d32890a51..f33eb1b81 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack Language Tools", "description": "A toolkit for building secure CRUD apps with Next.js + Typescript", - "version": "0.5.0", + "version": "1.0.0-alpha.27", "author": { "name": "ZenStack Team" }, @@ -15,12 +15,16 @@ "typescript", "data modeling" ], - "preview": true, + "preview": false, "icon": "asset/logo-256-bg.png", "repository": { "type": "git", "url": "https://github.com/zenstackhq/zenstack" }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, "engines": { "vscode": "^1.56.0" }, @@ -56,48 +60,44 @@ "activationEvents": [ "onLanguage:zmodel" ], - "files": [ - "bin", - "src", - "bundle" - ], "bin": { - "zenstack": "./bin/cli" + "zenstack": "bin/cli" }, "main": "./bundle/extension.js", "scripts": { "vscode:publish": "vsce publish --no-dependencies", - "vscode:prepublish": "cp ../../README.md ./ && pnpm lint && pnpm build", + "vscode:prepublish": "pnpm lint && pnpm bundle", "vscode:package": "vsce package --no-dependencies", - "clean": "rimraf bundle", - "build": "pnpm -C ../runtime build && pnpm langium:generate && tsc --noEmit && pnpm bundle && cp -r src/res/* bundle/res/", - "bundle": "npm run clean && node build/bundle.js --minify", - "bundle-watch": "node build/bundle.js --watch", - "ts:watch": "tsc --watch --noEmit", - "tsc-alias:watch": "tsc-alias --watch", - "lint": "eslint src --ext ts", - "langium:generate": "langium generate", - "langium:watch": "langium generate --watch", - "watch": "concurrently --kill-others \"npm:langium:watch\" \"npm:bundle-watch\"", + "clean": "rimraf bundle dist", + "build": "pnpm clean && pnpm lint && tsc && copyfiles -F \"bin/*\" dist && copyfiles ./README-global.md ./LICENSE ./package.json dist && renamer --replace \"README.md\" dist/README-global.md && copyfiles -u 1 \"src/res/*\" dist && node build/post-build.js", + "bundle": "pnpm clean && pnpm lint && node build/bundle.js --minify", + "watch": "tsc --watch", + "lint": "eslint src tests --ext ts", "test": "jest", - "prepublishOnly": "cp ../../README.md ./ && pnpm build", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm publish --tag dev", "postinstall": "node bin/post-install.js" }, "dependencies": { - "@zenstackhq/runtime": "workspace:../runtime/dist", + "@prisma/generator-helper": "^4.7.1", + "@prisma/internals": "^4.7.1", + "@zenstackhq/language": "workspace:*", + "@zenstackhq/runtime": "workspace:*", + "@zenstackhq/sdk": "workspace:*", "async-exit-hook": "^2.0.1", "change-case": "^4.1.2", "chevrotain": "^9.1.0", "colors": "1.4.0", "commander": "^8.3.0", "cuid": "^2.1.8", - "langium": "^0.5.0", + "langium": "^1.0.1", "mixpanel": "^0.17.0", "node-machine-id": "^1.1.12", - "ora": "^6.1.2", + "ora": "^5.4.1", "pluralize": "^8.0.0", "prisma": "~4.7.0", "promisify": "^0.0.3", + "semver": "^7.3.8", "sleep-promise": "^9.1.0", "ts-morph": "^16.0.0", "uuid": "^9.0.0", @@ -105,31 +105,34 @@ "vscode-languageclient": "^8.0.2", "vscode-languageserver": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.7", - "vscode-uri": "^3.0.6" + "vscode-uri": "^3.0.6", + "zod": "^3.19.1" }, "devDependencies": { - "@prisma/internals": "~4.7.0", "@types/async-exit-hook": "^2.0.0", "@types/jest": "^29.2.0", "@types/node": "^14.18.32", "@types/pluralize": "^0.0.29", + "@types/semver": "^7.3.13", "@types/tmp": "^0.2.3", "@types/uuid": "^8.3.4", "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/parser": "^5.42.0", "concurrently": "^7.4.0", + "copyfiles": "^2.4.1", "dotenv": "^16.0.3", "esbuild": "^0.15.12", "eslint": "^8.27.0", + "eslint-plugin-jest": "^27.1.7", "jest": "^29.2.1", - "langium-cli": "^0.5.0", + "langium-cli": "^1.0.0", + "renamer": "^4.0.0", "rimraf": "^3.0.2", "tmp": "^0.2.1", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "tsc-alias": "^1.7.0", - "tsconfig-paths-jest": "^0.0.1", "typescript": "^4.8.4", "vsce": "^2.13.0" } diff --git a/packages/schema/script/post-install.js b/packages/schema/script/post-install.js deleted file mode 100644 index 202c26bf1..000000000 --- a/packages/schema/script/post-install.js +++ /dev/null @@ -1,24 +0,0 @@ -try { - if (process.env.DO_NOT_TRACK == '1') { - process.exit(0); - } - - const Mixpanel = require('mixpanel'); - const machineId = require('node-machine-id'); - const os = require('os'); - - const mixpanel = Mixpanel.init('', { - geolocate: true, - }); - - const version = require('../package.json').version; - const payload = { - distinct_id: machineId.machineIdSync(), - nodeVersion: process.version, - time: new Date(), - $os: os.platform(), - version, - }; - - mixpanel.track('npm:install', payload); -} catch {} diff --git a/packages/schema/src/cli/cli-util.ts b/packages/schema/src/cli/cli-util.ts index 27f2619c7..78beb7ee0 100644 --- a/packages/schema/src/cli/cli-util.ts +++ b/packages/schema/src/cli/cli-util.ts @@ -1,118 +1,72 @@ -import { STD_LIB_MODULE_NAME } from '@lang/constants'; -import { Model } from '@lang/generated/ast'; -import { createZModelServices } from '@lang/zmodel-module'; +import { Model } from '@zenstackhq/language/ast'; +import { PluginError } from '@zenstackhq/sdk'; import colors from 'colors'; import fs from 'fs'; import { LangiumServices } from 'langium'; import { NodeFileSystem } from 'langium/node'; import path from 'path'; -import { installPackage, PackageManagers } from '../utils/pkg-utils'; import { URI } from 'vscode-uri'; -import { ZenStackGenerator } from '../generator'; -import { GENERATED_CODE_PATH } from '../generator/constants'; -import { Context, GeneratorError } from '../generator/types'; +import { STD_LIB_MODULE_NAME } from '../language-server/constants'; +import { createZModelServices } from '../language-server/zmodel-module'; +import { Context } from '../types'; +import { installPackage, PackageManagers } from '../utils/pkg-utils'; import { CliError } from './cli-error'; +import { PluginRunner } from './plugin-runner'; /** * Initializes an existing project for ZenStack */ export async function initProject( projectPath: string, - packageManager: PackageManagers | undefined + prismaSchema: string | undefined, + packageManager: PackageManagers | undefined, + tag: string ) { if (!fs.existsSync(projectPath)) { console.error(`Path does not exist: ${projectPath}`); throw new CliError('project path does not exist'); } - const schema = path.join(projectPath, 'zenstack', 'schema.zmodel'); - let schemaGenerated = false; - - if (fs.existsSync(schema)) { - console.warn(colors.yellow(`Model already exists: ${schema}`)); - } else { - // create a default model - if (!fs.existsSync(path.join(projectPath, 'zenstack'))) { - fs.mkdirSync(path.join(projectPath, 'zenstack')); + const defaultPrismaSchemaLocation = './prisma/schema.prisma'; + if (prismaSchema) { + if (!fs.existsSync(prismaSchema)) { + console.error(`Prisma schema file does not exist: ${prismaSchema}`); + throw new CliError('prisma schema does not exist'); } + } else if (fs.existsSync(defaultPrismaSchemaLocation)) { + prismaSchema = defaultPrismaSchemaLocation; + } - fs.writeFileSync( - schema, - `// This is a sample model to get you started. -// Learn how to model you app: https://zenstack.dev/#/modeling-your-app. - -/* - * A sample data source using local sqlite db. - * See how to use a different db: https://zenstack.dev/#/zmodel-data-source. - */ -datasource db { - provider = 'sqlite' - url = 'file:./todo.db' -} - -/* - * User model - */ -model User { - id String @id @default(cuid()) - email String @unique @email - password String @password @omit @length(8, 16) - posts Post[] - - // everybody can signup - @@allow('create', true) - - // full access by self - @@allow('all', auth() == this) -} - -/* - * Post model - */ -model Post { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - title String @length(1, 256) - content String - published Boolean @default(false) - author User? @relation(fields: [authorId], references: [id]) - authorId String? - - // allow read for all signin users - @@allow('read', auth() != null && published) - - // full access by author - @@allow('all', author == auth()) -} -` - ); - - // add zenstack/schema.prisma to .gitignore - const gitIgnorePath = path.join(projectPath, '.gitignore'); - let gitIgnoreContent = ''; - if (fs.existsSync(gitIgnorePath)) { - gitIgnoreContent = - fs.readFileSync(gitIgnorePath, { encoding: 'utf-8' }) + '\n'; - } + const zmodelFile = path.join(projectPath, './schema.zmodel'); + let sampleModelGenerated = false; - if (!gitIgnoreContent.includes('zenstack/schema.prisma')) { - gitIgnoreContent += 'zenstack/schema.prisma\n'; - fs.writeFileSync(gitIgnorePath, gitIgnoreContent); + if (fs.existsSync(zmodelFile)) { + console.warn(`ZenStack model already exists at ${zmodelFile}, not generating a new one.`); + } else { + if (prismaSchema) { + // copy over schema.prisma + fs.copyFileSync(prismaSchema, zmodelFile); + } else { + // create a new model + const starterContent = fs.readFileSync(path.join(__dirname, '../res/starter.zmodel'), 'utf-8'); + fs.writeFileSync(zmodelFile, starterContent); + sampleModelGenerated = true; } - - schemaGenerated = true; } - installPackage('zenstack', true, packageManager, projectPath); - installPackage('@zenstackhq/runtime', false, packageManager, projectPath); + installPackage('zenstack', true, packageManager, tag, projectPath); + installPackage('@zenstackhq/runtime', false, packageManager, tag, projectPath); - if (schemaGenerated) { - console.log(`Sample model generated at: ${colors.blue(schema)} + if (sampleModelGenerated) { + console.log(`Sample model generated at: ${colors.blue(zmodelFile)} - Please check the following guide on how to model your app: - https://zenstack.dev/#/modeling-your-app. - `); +Please check the following guide on how to model your app: + https://zenstack.dev/#/modeling-your-app.`); + } else { + console.log( + `Your current Prisma schema "${prismaSchema}" has been copied to "${zmodelFile}". +Moving forward please edit this file and run "zenstack generate" to regenerate Prisma schema.` + ); } console.log(colors.green('\nProject initialized successfully!')); @@ -124,15 +78,10 @@ model Post { * @param services Language services * @returns Parsed and validated AST */ -export async function loadDocument( - fileName: string, - services: LangiumServices -): Promise { +export async function loadDocument(fileName: string, services: LangiumServices): Promise { const extensions = services.LanguageMetaData.fileExtensions; if (!extensions.includes(path.extname(fileName))) { - console.error( - colors.yellow(`Please choose a file with extension: ${extensions}.`) - ); + console.error(colors.yellow(`Please choose a file with extension: ${extensions}.`)); throw new CliError('invalid schema file'); } @@ -142,29 +91,19 @@ export async function loadDocument( } // load standard library - const stdLib = - services.shared.workspace.LangiumDocuments.getOrCreateDocument( - URI.file( - path.resolve( - path.join(__dirname, '../res', STD_LIB_MODULE_NAME) - ) - ) - ); + const stdLib = services.shared.workspace.LangiumDocuments.getOrCreateDocument( + URI.file(path.resolve(path.join(__dirname, '../res', STD_LIB_MODULE_NAME))) + ); // load the document - const document = - services.shared.workspace.LangiumDocuments.getOrCreateDocument( - URI.file(path.resolve(fileName)) - ); + const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName))); // build the document together with standard library await services.shared.workspace.DocumentBuilder.build([stdLib, document], { validationChecks: 'all', }); - const validationErrors = (document.diagnostics ?? []).filter( - (e) => e.severity === 1 - ); + const validationErrors = (document.diagnostics ?? []).filter((e) => e.severity === 1); if (validationErrors.length > 0) { console.error(colors.red('Validation errors:')); for (const validationError of validationErrors) { @@ -182,29 +121,20 @@ export async function loadDocument( return document.parseResult.value as Model; } -export async function runGenerator( - options: { schema: string; packageManager: PackageManagers | undefined }, - includedGenerators?: string[], - clearOutput = true -) { +export async function runPlugins(options: { schema: string; packageManager: PackageManagers | undefined }) { const services = createZModelServices(NodeFileSystem).ZModel; const model = await loadDocument(options.schema, services); const context: Context = { schema: model, + schemaPath: path.resolve(options.schema), outDir: path.dirname(options.schema), - // TODO: make this configurable - generatedCodeDir: GENERATED_CODE_PATH, }; try { - await new ZenStackGenerator().generate( - context, - includedGenerators, - clearOutput - ); + await new PluginRunner().run(context); } catch (err) { - if (err instanceof GeneratorError) { + if (err instanceof PluginError) { console.error(colors.red(err.message)); throw new CliError(err.message); } else { diff --git a/packages/schema/src/cli/index.ts b/packages/schema/src/cli/index.ts index f7c8f6644..471f6e666 100644 --- a/packages/schema/src/cli/index.ts +++ b/packages/schema/src/cli/index.ts @@ -1,19 +1,23 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { paramCase } from 'change-case'; +import { ZModelLanguageMetaData } from '@zenstackhq/language/module'; import colors from 'colors'; import { Command, Option } from 'commander'; -import path from 'path'; -import { PackageManagers } from '../utils/pkg-utils'; -import { ZModelLanguageMetaData } from '../language-server/generated/module'; +import * as semver from 'semver'; import telemetry from '../telemetry'; -import { execSync } from '../utils/exec-utils'; +import { PackageManagers } from '../utils/pkg-utils'; +import { getVersion } from '../utils/version-utils'; import { CliError } from './cli-error'; -import { initProject, runGenerator } from './cli-util'; +import { initProject, runPlugins } from './cli-util'; + +// required minimal version of Prisma +export const requiredPrismaVersion = '4.0.0'; export const initAction = async ( projectPath: string, options: { + prisma: string | undefined; packageManager: PackageManagers | undefined; + tag: string; } ): Promise => { await telemetry.trackSpan( @@ -21,7 +25,7 @@ export const initAction = async ( 'cli:command:complete', 'cli:command:error', { command: 'init' }, - () => initProject(projectPath, options.packageManager) + () => initProject(projectPath, options.prisma, options.packageManager, options.tag) ); }; @@ -29,218 +33,92 @@ export const generateAction = async (options: { schema: string; packageManager: PackageManagers | undefined; }): Promise => { + checkRequiredPackage('prisma', requiredPrismaVersion); + checkRequiredPackage('@prisma/client', requiredPrismaVersion); await telemetry.trackSpan( 'cli:command:start', 'cli:command:complete', 'cli:command:error', { command: 'generate' }, - () => runGenerator(options) + () => runPlugins(options) ); }; -function prismaAction(prismaCmd: string): (...args: any[]) => Promise { - return async (options: any, command: Command) => { - await telemetry.trackSpan( - 'cli:command:start', - 'cli:command:complete', - 'cli:command:error', - { - command: prismaCmd - ? prismaCmd + ' ' + command.name() - : command.name(), - }, - async () => { - const optStr = Array.from(Object.entries(options)) - .map(([k, v]) => { - let optVal = v; - if (k === 'schema') { - optVal = path.join( - path.dirname(v), - 'schema.prisma' - ); - } - return ( - '--' + - paramCase(k) + - (typeof optVal === 'string' ? ` ${optVal}` : '') - ); - }) - .join(' '); - - // regenerate prisma schema first - await runGenerator(options, ['prisma'], false); - - const prismaExec = `npx prisma ${prismaCmd} ${command.name()} ${optStr}`; - console.log(prismaExec); - try { - execSync(prismaExec); - } catch { - telemetry.track('cli:command:error', { - command: prismaCmd, - }); - console.error( - colors.red( - 'Prisma command failed to execute. See errors above.' - ) - ); - throw new CliError('prisma command run error'); - } - } +const checkRequiredPackage = (packageName: string, minVersion?: string) => { + let packageVersion: string; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + packageVersion = require(`${packageName}/package.json`).version; + } catch (error) { + console.error(colors.red(`${packageName} not found, please install it`)); + throw new CliError(`${packageName} not found`); + } + + if (minVersion && semver.lt(packageVersion, minVersion)) { + console.error( + colors.red( + `${packageName} needs to be above ${minVersion}, the installed version is ${packageVersion}, please upgrade it` + ) ); - }; + throw new CliError(`${packageName} version is too low`); + } +}; + +export function createProgram() { + const program = new Command('zenstack'); + + program.version(getVersion(), '-v --version', 'display CLI version'); + + const schemaExtensions = ZModelLanguageMetaData.fileExtensions.join(', '); + + program + .description( + `${colors.bold.blue( + 'ζ' + )} ZenStack is a Prisma power pack for building full-stack apps.\n\nDocumentation: https://zenstack.dev.` + ) + .showHelpAfterError() + .showSuggestionAfterError(); + + const schemaOption = new Option('--schema ', `schema file (with extension ${schemaExtensions})`).default( + './schema.zmodel' + ); + + const pmOption = new Option('-p, --package-manager ', 'package manager to use').choices([ + 'npm', + 'yarn', + 'pnpm', + ]); + + program + .command('init') + .description('Initialize an existing project for ZenStack.') + .addOption(pmOption) + .addOption(new Option('--prisma ', 'location of Prisma schema file to bootstrap from')) + .addOption( + new Option('--tag ', 'the NPM package tag to use when installing dependencies').default( + '' + ) + ) + .argument('[path]', 'project path', '.') + .action(initAction); + + program + .command('generate') + .description('Generates RESTful API and Typescript client for your data model.') + .addOption(schemaOption) + .addOption(pmOption) + .action(generateAction); + return program; } export default async function (): Promise { - await telemetry.trackSpan( - 'cli:start', - 'cli:complete', - 'cli:error', - { args: process.argv }, - async () => { - const program = new Command('zenstack'); - - program.version( - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('../../package.json').version, - '-v --version', - 'display CLI version' - ); - - const schemaExtensions = - ZModelLanguageMetaData.fileExtensions.join(', '); - - program - .description( - `${colors.bold.blue( - 'ζ' - )} ZenStack is a toolkit for building secure CRUD apps with Next.js + Typescript.\n\nDocumentation: https://zenstack.dev.` - ) - .showHelpAfterError() - .showSuggestionAfterError(); - - const schemaOption = new Option( - '--schema ', - `schema file (with extension ${schemaExtensions})` - ).default('./zenstack/schema.zmodel'); - - const pmOption = new Option( - '-p, --package-manager ', - 'package manager to use' - ).choices(['npm', 'yarn', 'pnpm']); - - //#region wraps Prisma commands - - program - .command('init') - .description('Set up a new ZenStack project.') - .addOption(pmOption) - .argument('[path]', 'project path', '.') - .action(initAction); - - program - .command('generate') - .description( - 'Generates RESTful API and Typescript client for your data model.' - ) - .addOption(schemaOption) - .addOption(pmOption) - .action(generateAction); - - const migrate = program - .command('migrate') - .description( - `Updates the database schema with migrations\nAlias for ${colors.cyan( - 'prisma migrate' - )}.` - ); - - migrate - .command('dev') - .description( - `Creates a migration, apply it to the database, generate db client\nAlias for ${colors.cyan( - 'prisma migrate dev' - )}.` - ) - .addOption(schemaOption) - .option( - '--create-only', - 'Create a migration without applying it' - ) - .option('-n --name ', 'Name the migration') - .option('--skip-seed', 'Skip triggering seed') - .action(prismaAction('migrate')); - - migrate - .command('reset') - .description( - `Resets your database and apply all migrations\nAlias for ${colors.cyan( - 'prisma migrate reset' - )}.` - ) - .addOption(schemaOption) - .option('--force', 'Skip the confirmation prompt') - .action(prismaAction('migrate')); - - migrate - .command('deploy') - .description( - `Applies pending migrations to the database in production/staging\nAlias for ${colors.cyan( - 'prisma migrate deploy' - )}.` - ) - .addOption(schemaOption) - .action(prismaAction('migrate')); - - migrate - .command('status') - .description( - `Checks the status of migrations in the production/staging database\nAlias for ${colors.cyan( - 'prisma migrate status' - )}.` - ) - .addOption(schemaOption) - .action(prismaAction('migrate')); - - const db = program - .command('db') - .description( - `Manages your database schema and lifecycle during development\nAlias for ${colors.cyan( - 'prisma db' - )}.` - ); - - db.command('push') - .description( - `Pushes the Prisma schema state to the database\nAlias for ${colors.cyan( - 'prisma db push' - )}.` - ) - .addOption(schemaOption) - .option('--accept-data-loss', 'Ignore data loss warnings') - .action(prismaAction('db')); - - program - .command('studio') - .description( - `Browses your data with Prisma Studio\nAlias for ${colors.cyan( - 'prisma studio' - )}.` - ) - .addOption(schemaOption) - .option('-p --port ', 'Port to start Studio in') - .option('-b --browser ', 'Browser to open Studio in') - .option( - '-n --hostname', - 'Hostname to bind the Express server to' - ) - .action(prismaAction('')); - - //#endregion - - // handle errors explicitly to ensure telemetry - program.exitOverride(); - - await program.parseAsync(process.argv); - } - ); + await telemetry.trackSpan('cli:start', 'cli:complete', 'cli:error', { args: process.argv }, async () => { + const program = createProgram(); + + // handle errors explicitly to ensure telemetry + program.exitOverride(); + + await program.parseAsync(process.argv); + }); } diff --git a/packages/schema/src/cli/plugin-runner.ts b/packages/schema/src/cli/plugin-runner.ts new file mode 100644 index 000000000..82b67b573 --- /dev/null +++ b/packages/schema/src/cli/plugin-runner.ts @@ -0,0 +1,160 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { DMMF } from '@prisma/generator-helper'; +import { getDMMF } from '@prisma/internals'; +import { Plugin, isPlugin } from '@zenstackhq/language/ast'; +import { PluginFunction, PluginOptions, getLiteral, getLiteralArray } from '@zenstackhq/sdk'; +import colors from 'colors'; +import fs from 'fs'; +import ora from 'ora'; +import path from 'path'; +import telemetry from '../telemetry'; +import { Context } from '../types'; +import { CliError } from './cli-error'; + +/** + * ZenStack code generator + */ +export class PluginRunner { + /** + * Runs a series of nested generators + */ + async run(context: Context): Promise { + const version = require('../package.json').version; + console.log(colors.bold(`⌛️ ZenStack CLI v${version}, running plugins`)); + + const plugins: Array<{ + provider: string; + name: string; + run: PluginFunction; + options: PluginOptions; + }> = []; + + const pluginDecls = context.schema.declarations.filter((d): d is Plugin => isPlugin(d)); + const prereqPlugins = ['@zenstack/prisma', '@zenstack/model-meta', '@zenstack/access-policy']; + const allPluginProviders = prereqPlugins.concat( + pluginDecls + .map((p) => this.getPluginProvider(p)) + .filter((p): p is string => !!p && !prereqPlugins.includes(p)) + ); + let prismaOutput = './prisma/schema.prisma'; + + for (const pluginProvider of allPluginProviders) { + const plugin = pluginDecls.find((p) => this.getPluginProvider(p) === pluginProvider); + if (plugin) { + const options: PluginOptions = { schemaPath: context.schemaPath }; + + plugin.fields.forEach((f) => { + const value = getLiteral(f.value) || getLiteralArray(f.value); + if (!value) { + throw new CliError(`Invalid plugin value for ${f.name}`); + } + options[f.name] = value; + }); + + const pluginModulePath = this.getPluginModulePath(pluginProvider); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let pluginModule: any; + try { + pluginModule = require(pluginModulePath); + } catch (err) { + console.error(`Unable to load plugin module ${pluginProvider}: ${pluginModulePath}, ${err}`); + throw new CliError(`Unable to load plugin module ${pluginProvider}`); + } + + if (!pluginModule.default || typeof pluginModule.default !== 'function') { + console.error(`Plugin provider ${pluginProvider} is missing a default function export`); + throw new CliError(`Plugin provider ${pluginProvider} is missing a default function export`); + } + plugins.push({ + name: this.getPluginName(pluginModule, pluginProvider), + provider: pluginProvider, + run: pluginModule.default as PluginFunction, + options, + }); + + if (pluginProvider === '@zenstack/prisma' && options.output) { + // record custom prisma output path + prismaOutput = options.output as string; + } + } else { + // synthesize a plugin + const pluginModule = require(this.getPluginModulePath(pluginProvider)); + plugins.push({ + name: this.getPluginName(pluginModule, pluginProvider), + provider: pluginProvider, + run: pluginModule.default, + options: { schemaPath: context.schemaPath }, + }); + } + } + + const warnings: string[] = []; + + let dmmf: DMMF.Document | undefined = undefined; + for (const { name, provider, run, options } of plugins) { + await this.runPlugin(name, run, context, options, dmmf, warnings); + if (provider === '@zenstack/prisma') { + // load prisma DMMF + dmmf = await getDMMF({ + datamodel: fs.readFileSync(prismaOutput, { encoding: 'utf-8' }), + }); + } + } + + console.log(colors.green(colors.bold('\n👻 All plugins completed successfully!'))); + + warnings.forEach((w) => console.warn(colors.yellow(w))); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private getPluginName(pluginModule: any, pluginProvider: string): string { + return typeof pluginModule.name === 'string' ? (pluginModule.name as string) : pluginProvider; + } + + private getPluginProvider(plugin: Plugin) { + const providerField = plugin.fields.find((f) => f.name === 'provider'); + return getLiteral(providerField?.value); + } + + private async runPlugin( + name: string, + run: PluginFunction, + context: Context, + options: PluginOptions, + dmmf: DMMF.Document | undefined, + warnings: string[] + ) { + const spinner = ora(`Running plugin ${colors.cyan(name)}`).start(); + try { + await telemetry.trackSpan( + 'cli:plugin:start', + 'cli:plugin:complete', + 'cli:plugin:error', + { + plugin: name, + }, + async () => { + let result = run(context.schema, options, dmmf); + if (result instanceof Promise) { + result = await result; + } + if (Array.isArray(result)) { + warnings.push(...result); + } + } + ); + spinner.succeed(); + } catch (err) { + spinner.fail(); + throw err; + } + } + + private getPluginModulePath(provider: string) { + let pluginModulePath = provider; + if (pluginModulePath.startsWith('@zenstack/')) { + pluginModulePath = pluginModulePath.replace(/^@zenstack/, path.join(__dirname, '../plugins')); + } + return pluginModulePath; + } +} diff --git a/packages/schema/src/constants.ts b/packages/schema/src/constants.ts new file mode 100644 index 000000000..586537af1 --- /dev/null +++ b/packages/schema/src/constants.ts @@ -0,0 +1,2 @@ +// replaced at build time +export const TELEMETRY_TRACKING_TOKEN = ''; diff --git a/packages/schema/src/extension.ts b/packages/schema/src/extension.ts index d2129a387..d28f7dd87 100644 --- a/packages/schema/src/extension.ts +++ b/packages/schema/src/extension.ts @@ -1,11 +1,6 @@ import * as vscode from 'vscode'; import * as path from 'path'; -import { - LanguageClient, - LanguageClientOptions, - ServerOptions, - TransportKind, -} from 'vscode-languageclient/node'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; let client: LanguageClient; @@ -23,18 +18,14 @@ export function deactivate(): Thenable | undefined { } function startLanguageClient(context: vscode.ExtensionContext): LanguageClient { - const serverModule = context.asAbsolutePath( - path.join('bundle', 'language-server', 'main') - ); + const serverModule = context.asAbsolutePath(path.join('bundle', 'language-server', 'main')); // The debug options for the server // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging. // By setting `process.env.DEBUG_BREAK` to a truthy value, the language server will wait until a debugger is attached. const debugOptions = { execArgv: [ '--nolazy', - `--inspect${process.env.DEBUG_BREAK ? '-brk' : ''}=${ - process.env.DEBUG_SOCKET || '6009' - }`, + `--inspect${process.env.DEBUG_BREAK ? '-brk' : ''}=${process.env.DEBUG_SOCKET || '6009'}`, ], }; @@ -49,8 +40,7 @@ function startLanguageClient(context: vscode.ExtensionContext): LanguageClient { }, }; - const fileSystemWatcher = - vscode.workspace.createFileSystemWatcher('**/*.zmodel'); + const fileSystemWatcher = vscode.workspace.createFileSystemWatcher('**/*.zmodel'); context.subscriptions.push(fileSystemWatcher); // Options to control the language client @@ -63,12 +53,7 @@ function startLanguageClient(context: vscode.ExtensionContext): LanguageClient { }; // Create the language client and start the client. - const client = new LanguageClient( - 'zmodel', - 'ZenStack Model', - serverOptions, - clientOptions - ); + const client = new LanguageClient('zmodel', 'ZenStack Model', serverOptions, clientOptions); // Start the client. This will also launch the server client.start(); diff --git a/packages/schema/src/generator/ast-utils.ts b/packages/schema/src/generator/ast-utils.ts deleted file mode 100644 index 5cc80bdb4..000000000 --- a/packages/schema/src/generator/ast-utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DataModel, isDataModel, Model } from '@lang/generated/ast'; -import { AstNode, Reference } from 'langium'; -import { GeneratorError } from './types'; - -export function extractDataModelsWithAllowRules(model: Model): DataModel[] { - return model.declarations.filter( - (d) => - isDataModel(d) && - !!d.attributes.find((attr) => attr.decl.ref?.name === '@@allow') - ) as DataModel[]; -} - -export function resolved(ref: Reference): T { - if (!ref.ref) { - throw new GeneratorError(`Reference not resolved: ${ref.$refText}`); - } - return ref.ref; -} diff --git a/packages/schema/src/generator/constants.ts b/packages/schema/src/generator/constants.ts deleted file mode 100644 index 165ed1898..000000000 --- a/packages/schema/src/generator/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const RUNTIME_PACKAGE = '@zenstackhq/runtime'; -export const GUARD_FIELD_NAME = 'zenstack_guard'; -export const TRANSACTION_FIELD_NAME = 'zenstack_transaction'; -export const API_ROUTE_NAME = 'zenstack'; -export const GENERATED_CODE_PATH = 'node_modules/.zenstack'; -export const UNKNOWN_USER_ID = 'zenstack_unknown_user'; diff --git a/packages/schema/src/generator/field-constraint/index.ts b/packages/schema/src/generator/field-constraint/index.ts deleted file mode 100644 index 35d023722..000000000 --- a/packages/schema/src/generator/field-constraint/index.ts +++ /dev/null @@ -1,304 +0,0 @@ -import { - DataModel, - DataModelField, - DataModelFieldAttribute, - isDataModel, - isLiteralExpr, - LiteralExpr, -} from '@lang/generated/ast'; -import * as path from 'path'; -import { Project, SourceFile } from 'ts-morph'; -import { Context, Generator } from '../types'; - -/** - * Generates field constraint validators (run on both client and server side) - */ -export default class FieldConstraintGenerator implements Generator { - get name() { - return 'field-constraint'; - } - - get startMessage() { - return 'Generating field constraints...'; - } - - get successMessage() { - return 'Successfully generated field constraints'; - } - - async generate(context: Context) { - const project = new Project(); - const sf = project.createSourceFile( - path.join( - context.generatedCodeDir, - 'src/field-constraint/index.ts' - ), - undefined, - { overwrite: true } - ); - - sf.addStatements([`import { z } from "zod";`]); - - context.schema.declarations - .filter((d): d is DataModel => isDataModel(d)) - .forEach((model) => { - this.generateConstraints(sf, model); - }); - - sf.formatText(); - await project.save(); - - return []; - } - - private generateConstraints(sf: SourceFile, model: DataModel) { - sf.addStatements(` - export const ${this.validator( - model.name, - 'create' - )}: z.ZodType = z.lazy(() => z.object({ - ${model.fields - .map((f) => ({ - field: f, - schema: this.makeFieldValidator(f, 'create'), - })) - .filter(({ schema }) => !!schema) - .map(({ field, schema }) => field.name + ': ' + schema) - .join(',\n')} - })); - - export const ${this.validator( - model.name, - 'update' - )}: z.ZodType = z.lazy(() => z.object({ - ${model.fields - .map((f) => ({ - field: f, - schema: this.makeFieldValidator(f, 'update'), - })) - .filter(({ schema }) => !!schema) - .map(({ field, schema }) => field.name + ': ' + schema) - .join(',\n')} - }).partial()); - `); - } - - private makeFieldValidator( - field: DataModelField, - mode: 'create' | 'update' - ) { - const baseSchema = this.makeZodSchema(field, mode); - let zodSchema = baseSchema; - - // translate field constraint attributes to zod schema - for (const attr of field.attributes) { - switch (attr.decl.ref?.name) { - case '@length': { - const min = this.getAttrLiteralArg(attr, 'min'); - if (min) { - zodSchema += `.min(${min})`; - } - const max = this.getAttrLiteralArg(attr, 'max'); - if (max) { - zodSchema += `.max(${max})`; - } - break; - } - case '@regex': { - const expr = this.getAttrLiteralArg(attr, 'regex'); - if (expr) { - zodSchema += `.regex(/${expr}/)`; - } - break; - } - case '@startsWith': { - const text = this.getAttrLiteralArg(attr, 'text'); - if (text) { - zodSchema += `.startsWith(${JSON.stringify(text)})`; - } - break; - } - case '@endsWith': { - const text = this.getAttrLiteralArg(attr, 'text'); - if (text) { - zodSchema += `.endsWith(${JSON.stringify(text)})`; - } - break; - } - case '@email': { - zodSchema += `.email()`; - break; - } - case '@url': { - zodSchema += `.url()`; - break; - } - case '@datetime': { - zodSchema += `.datetime({ offset: true })`; - break; - } - case '@gt': { - const value = this.getAttrLiteralArg(attr, 'value'); - if (value !== undefined) { - zodSchema += `.gt(${value})`; - } - break; - } - case '@gte': { - const value = this.getAttrLiteralArg(attr, 'value'); - if (value !== undefined) { - zodSchema += `.gte(${value})`; - } - break; - } - case '@lt': { - const value = this.getAttrLiteralArg(attr, 'value'); - if (value !== undefined) { - zodSchema += `.lt(${value})`; - } - break; - } - case '@lte': { - const value = this.getAttrLiteralArg(attr, 'value'); - if (value !== undefined) { - zodSchema += `.lte(${value})`; - } - break; - } - } - } - - if ( - !isDataModel(field.type.reference?.ref) && - zodSchema === baseSchema - ) { - // empty schema, skip - return undefined; - } - - if (field.type.optional) { - zodSchema = this.optional(zodSchema); - } - - return zodSchema; - } - - private getAttrLiteralArg( - attr: DataModelFieldAttribute, - paramName: string - ) { - const arg = attr.args.find( - (arg) => arg.$resolvedParam?.name === paramName - ); - if (!arg || !isLiteralExpr(arg.value)) { - return undefined; - } - return (arg.value as LiteralExpr).value as T; - } - - private makeZodSchema(field: DataModelField, mode: 'create' | 'update') { - const type = field.type; - let schema = ''; - if (type.reference && isDataModel(type.reference.ref)) { - const modelType = type.reference.ref.name; - const create = this.validator(modelType, 'create'); - const update = this.validator(modelType, 'update'); - - // list all possible action fields in write playload: - // create/createMany/connectOrCreate/update/updateMany/upsert - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let fields: any = { - create: this.optional(this.enumerable(create)), - createMany: this.optional(this.enumerable(create)), - connectOrCreate: this.optional( - this.enumerable(this.object({ create })) - ), - }; - - if (mode === 'update') { - fields = { - ...fields, - update: this.optional( - this.enumerable( - type.array ? this.object({ data: update }) : update - ) - ), - updateMany: this.optional( - this.enumerable(this.object({ data: update })) - ), - upsert: this.optional( - type.array - ? this.enumerable( - this.object({ - create, - update, - }) - ) - : this.object({ - create, - update, - }) - ), - }; - } - - schema = this.optional(this.object(fields)); - } else { - switch (type.type) { - case 'Int': - case 'Float': - case 'Decimal': - schema = 'z.number()'; - break; - case 'BigInt': - schema = 'z.bigint()'; - break; - case 'String': - schema = 'z.string()'; - break; - case 'Boolean': - schema = 'z.boolean()'; - break; - case 'DateTime': - schema = 'z.date()'; - break; - default: - schema = 'z.any()'; - break; - } - - if (type.array) { - schema = this.array(schema); - } - } - - return schema; - } - - private union(...schemas: string[]) { - return `z.union([${schemas.join(', ')}])`; - } - - private optional(schema: string) { - return `z.optional(${schema})`; - } - - private array(schema: string) { - return `z.array(${schema})`; - } - - private enumerable(schema: string) { - return this.union(schema, this.array(schema)); - } - - private object(fields: Record) { - return `z.object({ ${Object.entries(fields) - .map(([k, v]) => k + ': ' + v) - .join(',\n')} })`; - } - - private validator(modelName: string, mode: 'create' | 'update') { - return `${modelName}_${mode}_validator`; - } -} diff --git a/packages/schema/src/generator/index.ts b/packages/schema/src/generator/index.ts deleted file mode 100644 index ff01c6ac8..000000000 --- a/packages/schema/src/generator/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { Context, Generator } from './types'; -import * as fs from 'fs'; -import colors from 'colors'; -import PrismaGenerator from './prisma'; -import ServiceGenerator from './service'; -import ReactHooksGenerator from './react-hooks'; -import { TypescriptCompilation } from './tsc'; -import FieldConstraintGenerator from './field-constraint'; -import telemetry from '../telemetry'; -import ora from 'ora'; - -/** - * ZenStack code generator - */ -export class ZenStackGenerator { - /** - * Runs a series of nested generators - */ - async generate( - context: Context, - includeGenerators?: string[], - clearOutput = true - ): Promise { - // ensure folder that stores generated prisma schema and migrations - if (!fs.existsSync(context.outDir)) { - fs.mkdirSync(context.outDir); - } - - if (clearOutput) { - // recreate folder that stores generated zenstack code - if (fs.existsSync(context.generatedCodeDir)) { - fs.rmSync(context.generatedCodeDir, { - force: true, - recursive: true, - }); - } - fs.mkdirSync(context.generatedCodeDir); - } - - // TODO: plugin mechanism - const generators: Generator[] = [ - new PrismaGenerator(), - new ServiceGenerator(), - new ReactHooksGenerator(), - new FieldConstraintGenerator(), - new TypescriptCompilation(), - ]; - - const version = require('../../package.json').version; - console.log(colors.bold(`⌛️ Running ZenStack generator v${version}`)); - - const warnings: string[] = []; - for (const generator of generators) { - if ( - includeGenerators && - !includeGenerators.includes(generator.name) - ) { - continue; - } - - const spinner = ora(generator.startMessage).start(); - await telemetry.trackSpan( - 'cli:generator:start', - 'cli:generator:complete', - 'cli:generator:error', - { - generator: generator.name, - }, - async () => { - const genWarnings = await generator.generate(context); - warnings.push(...genWarnings); - } - ); - spinner.succeed(`${colors.cyan(generator.successMessage)}`); - } - - console.log( - colors.green( - colors.bold('👻 All generators completed successfully!') - ) - ); - - warnings.forEach((w) => console.warn(colors.yellow(w))); - } -} diff --git a/packages/schema/src/generator/prisma/index.ts b/packages/schema/src/generator/prisma/index.ts deleted file mode 100644 index 46ee852db..000000000 --- a/packages/schema/src/generator/prisma/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { execSync } from '../../utils/exec-utils'; -import { Context, Generator, GeneratorError } from '../types'; -import QueryGuardGenerator from './query-guard-generator'; -import PrismaSchemaGenerator from './schema-generator'; - -/** - * Generates Prisma schema and db client - */ -export default class PrismaGenerator implements Generator { - get name() { - return 'prisma'; - } - - get startMessage() { - return 'Generating Prisma client...'; - } - - get successMessage() { - return 'Successfully generated Prisma client'; - } - - async generate(context: Context) { - // generate prisma schema - const schemaFile = await new PrismaSchemaGenerator(context).generate(); - - // run prisma generate and install @prisma/client - await this.generatePrismaClient(schemaFile); - - // generate prisma query guard - await new QueryGuardGenerator(context).generate(); - - return []; - } - - private async generatePrismaClient(schemaFile: string) { - try { - execSync(`npx prisma generate --schema "${schemaFile}"`); - } catch { - throw new GeneratorError( - `Failed to generate client code with Prisma. Check errors above for clues.\nThis normally shouldn't happen. Please file an issue at: http://go.zenstack.dev/bug.` - ); - } - } -} diff --git a/packages/schema/src/generator/prisma/query-guard-generator.ts b/packages/schema/src/generator/prisma/query-guard-generator.ts deleted file mode 100644 index f71f8360d..000000000 --- a/packages/schema/src/generator/prisma/query-guard-generator.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { - DataModel, - DataModelField, - isDataModel, - isEnum, - isLiteralExpr, -} from '@lang/generated/ast'; -import { - FieldInfo, - PolicyKind, - PolicyOperationKind, - RuntimeAttribute, -} from '@zenstackhq/runtime/server'; -import path from 'path'; -import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph'; -import { - GUARD_FIELD_NAME, - RUNTIME_PACKAGE, - UNKNOWN_USER_ID, -} from '../constants'; -import { Context } from '../types'; -import { resolved } from '../ast-utils'; -import ExpressionWriter from './expression-writer'; - -/** - * Generates source file that contains Prisma query guard objects used for injecting database queries - */ -export default class QueryGuardGenerator { - constructor(private readonly context: Context) {} - - async generate(): Promise { - const project = new Project(); - const sf = project.createSourceFile( - path.join(this.context.generatedCodeDir, 'src/query/guard.ts'), - undefined, - { overwrite: true } - ); - - sf.addImportDeclaration({ - namedImports: [{ name: 'QueryContext' }], - moduleSpecifier: `${RUNTIME_PACKAGE}/server`, - isTypeOnly: true, - }); - - // import enums - for (const e of this.context.schema.declarations.filter((d) => - isEnum(d) - )) { - sf.addImportDeclaration({ - namedImports: [{ name: e.name }], - moduleSpecifier: '../../.prisma', - }); - } - - const models = this.context.schema.declarations.filter((d) => - isDataModel(d) - ) as DataModel[]; - - this.generateFieldMapping(models, sf); - - for (const model of models) { - await this.generateQueryGuardForModel(model, sf); - } - - sf.formatText({}); - await project.save(); - } - - private generateFieldMapping(models: DataModel[], sourceFile: SourceFile) { - const mapping = Object.fromEntries( - models.map((m) => [ - m.name, - Object.fromEntries( - m.fields.map((f) => { - const fieldInfo: FieldInfo = { - name: f.name, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - type: f.type.reference - ? f.type.reference.$refText - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - f.type.type!, - isDataModel: isDataModel(f.type.reference?.ref), - isArray: f.type.array, - isOptional: f.type.optional, - attributes: this.getFieldAttributes(f), - }; - return [f.name, fieldInfo]; - }) - ), - ]) - ); - - sourceFile.addVariableStatement({ - isExported: true, - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: '_fieldMapping', - initializer: JSON.stringify(mapping), - }, - ], - }); - } - - private getFieldAttributes(field: DataModelField): RuntimeAttribute[] { - return field.attributes - .map((attr) => { - const args: Array<{ name?: string; value: unknown }> = []; - for (const arg of attr.args) { - if (!isLiteralExpr(arg.value)) { - // attributes with non-literal args are skipped - return undefined; - } - args.push({ name: arg.name, value: arg.value.value }); - } - return { name: resolved(attr.decl).name, args }; - }) - .filter((d): d is RuntimeAttribute => !!d); - } - - private getPolicyExpressions( - model: DataModel, - kind: PolicyKind, - operation: PolicyOperationKind - ) { - const attrs = model.attributes.filter( - (attr) => attr.decl.ref?.name === `@@${kind}` - ); - return attrs - .filter((attr) => { - if ( - !isLiteralExpr(attr.args[0].value) || - typeof attr.args[0].value.value !== 'string' - ) { - return false; - } - const ops = attr.args[0].value.value - .split(',') - .map((s) => s.trim()); - return ops.includes(operation) || ops.includes('all'); - }) - .map((attr) => attr.args[1].value); - } - - private async generateQueryGuardForModel( - model: DataModel, - sourceFile: SourceFile - ) { - for (const kind of ['create', 'update', 'read', 'delete']) { - const func = sourceFile - .addFunction({ - name: model.name + '_' + kind, - returnType: 'any', - parameters: [ - { - name: 'context', - type: 'QueryContext', - }, - ], - isExported: true, - }) - .addBody(); - - func.addStatements( - // make suer user id is always available - `const user = context.user?.id ? context.user : { ...context.user, id: '${UNKNOWN_USER_ID}' };` - ); - - // r = ; - func.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'r', - initializer: (writer) => { - const exprWriter = new ExpressionWriter(writer); - const denies = this.getPolicyExpressions( - model, - 'deny', - kind as PolicyOperationKind - ); - const allows = this.getPolicyExpressions( - model, - 'allow', - kind as PolicyOperationKind - ); - - const writeDenies = () => { - writer.conditionalWrite( - denies.length > 1, - '{ AND: [' - ); - denies.forEach((expr, i) => { - writer.block(() => { - writer.write('NOT: '); - exprWriter.write(expr); - }); - writer.conditionalWrite( - i !== denies.length - 1, - ',' - ); - }); - writer.conditionalWrite( - denies.length > 1, - ']}' - ); - }; - - const writeAllows = () => { - writer.conditionalWrite( - allows.length > 1, - '{ OR: [' - ); - allows.forEach((expr, i) => { - exprWriter.write(expr); - writer.conditionalWrite( - i !== allows.length - 1, - ',' - ); - }); - writer.conditionalWrite( - allows.length > 1, - ']}' - ); - }; - - if (allows.length > 0 && denies.length > 0) { - writer.writeLine('{ AND: ['); - writeDenies(); - writer.writeLine(','); - writeAllows(); - writer.writeLine(']}'); - } else if (denies.length > 0) { - writeDenies(); - } else if (allows.length > 0) { - writeAllows(); - } else { - // disallow any operation - writer.write(`{ ${GUARD_FIELD_NAME}: false }`); - } - }, - }, - ], - }); - - func.addStatements('return r;'); - } - } -} diff --git a/packages/schema/src/generator/react-hooks/index.ts b/packages/schema/src/generator/react-hooks/index.ts deleted file mode 100644 index 7a856bedd..000000000 --- a/packages/schema/src/generator/react-hooks/index.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { DataModel, isDataModel } from '@lang/generated/ast'; -import { paramCase } from 'change-case'; -import * as path from 'path'; -import { Project } from 'ts-morph'; -import { API_ROUTE_NAME, RUNTIME_PACKAGE } from '../constants'; -import { Context, Generator } from '../types'; - -/** - * Generate react data query hooks code - */ -export default class ReactHooksGenerator implements Generator { - get name() { - return 'react-hooks'; - } - - get startMessage() { - return 'Generating React hooks...'; - } - - get successMessage(): string { - return 'Successfully generated React hooks'; - } - - async generate(context: Context) { - const project = new Project(); - const models: DataModel[] = []; - const warnings: string[] = []; - - for (const model of context.schema.declarations.filter( - (d): d is DataModel => isDataModel(d) - )) { - const hasAllowRule = model.attributes.find( - (attr) => attr.decl.ref?.name === '@@allow' - ); - if (!hasAllowRule) { - warnings.push( - `Not generating hooks for "${model.name}" because it doesn't have any @@allow rule` - ); - } else { - models.push(model); - } - } - - this.generateIndex(project, context, models); - - models.forEach((d) => this.generateModelHooks(project, context, d)); - - await project.save(); - return warnings; - } - - private getValidator(model: DataModel, mode: 'create' | 'update') { - return `${model.name}_${mode}_validator`; - } - - private generateModelHooks( - project: Project, - context: Context, - model: DataModel - ) { - const fileName = paramCase(model.name); - const sf = project.createSourceFile( - path.join(context.generatedCodeDir, `src/hooks/${fileName}.ts`), - undefined, - { overwrite: true } - ); - - sf.addImportDeclaration({ - namedImports: [{ name: 'Prisma', alias: 'P' }, model.name], - isTypeOnly: true, - moduleSpecifier: '../../.prisma', - }); - sf.addStatements([ - `import * as request from '${RUNTIME_PACKAGE}/lib/request';`, - `import { ServerErrorCode, RequestOptions } from '${RUNTIME_PACKAGE}/lib/types';`, - `import { validate } from '${RUNTIME_PACKAGE}/lib/validation';`, - `import { type SWRResponse } from 'swr';`, - `import { ${this.getValidator( - model, - 'create' - )}, ${this.getValidator( - model, - 'update' - )} } from '../field-constraint';`, - ]); - - sf.addStatements( - `const endpoint = '/api/${API_ROUTE_NAME}/data/${model.name}';` - ); - - const useFuncBody = sf - .addFunction({ - name: `use${model.name}`, - isExported: true, - }) - .addBody(); - - useFuncBody.addStatements(['const mutate = request.getMutate();']); - - // create - useFuncBody - .addFunction({ - name: 'create', - isAsync: true, - typeParameters: [`T extends P.${model.name}CreateArgs`], - parameters: [ - { name: 'args', type: `P.${model.name}CreateArgs` }, - ], - }) - .addBody() - .addStatements([ - ` - // validate field-level constraints - validate(${this.getValidator(model, 'create')}, args.data); - - try { - return await request.post>>(endpoint, args, mutate); - } catch (err: any) { - if (err.info?.code === ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED) { - return undefined; - } else { - throw err; - } - } - `, - ]); - - // find - useFuncBody - .addFunction({ - name: 'find', - typeParameters: [`T extends P.${model.name}FindManyArgs`], - returnType: `SWRResponse[]>, any>`, - parameters: [ - { - name: 'args?', - type: `P.SelectSubset`, - }, - { - name: 'options?', - type: `RequestOptions, Array>>>`, - }, - ], - }) - .addBody() - .addStatements([ - `return request.get, Array>>>(endpoint, args, options);`, - ]); - - // get - useFuncBody - .addFunction({ - name: 'get', - typeParameters: [`T extends P.${model.name}FindFirstArgs`], - returnType: `SWRResponse>, any>`, - parameters: [ - { - name: 'id', - type: 'String | undefined', - }, - { - name: 'args?', - type: `P.SelectSubset>`, - }, - { - name: 'options?', - type: `RequestOptions>>`, - }, - ], - }) - .addBody() - .addStatements([ - `return request.get>>(id ? \`\${endpoint}/\${id}\`: null, args, options);`, - ]); - - // update - useFuncBody - .addFunction({ - name: 'update', - isAsync: true, - typeParameters: [ - `T extends Omit`, - ], - parameters: [ - { name: 'id', type: 'String' }, - { - name: 'args', - type: `Omit`, - }, - ], - }) - .addBody() - .addStatements([ - ` - // validate field-level constraints - validate(${this.getValidator(model, 'update')}, args.data); - - try { - return await request.put, P.CheckSelect>>(\`\${endpoint}/\${id}\`, args, mutate); - } catch (err: any) { - if (err.info?.code === ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED) { - return undefined; - } else { - throw err; - } - } - `, - ]); - - // del - useFuncBody - .addFunction({ - name: 'del', - isAsync: true, - typeParameters: [ - `T extends Omit`, - ], - parameters: [ - { name: 'id', type: 'String' }, - { - name: 'args?', - type: `Omit`, - }, - ], - }) - .addBody() - .addStatements([ - ` - try { - return await request.del>>(\`\${endpoint}/\${id}\`, args, mutate); - } catch (err: any) { - if (err.info?.code === ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED) { - return undefined; - } else { - throw err; - } - } - `, - ]); - - useFuncBody.addStatements([ - 'return { create, find, get, update, del };', - ]); - - sf.formatText(); - } - - private generateIndex( - project: Project, - context: Context, - models: DataModel[] - ) { - const sf = project.createSourceFile( - path.join(context.generatedCodeDir, 'src/hooks/index.ts'), - undefined, - { overwrite: true } - ); - - sf.addStatements( - models.map((d) => `export * from './${paramCase(d.name)}';`) - ); - - sf.formatText(); - } -} diff --git a/packages/schema/src/generator/service/index.ts b/packages/schema/src/generator/service/index.ts deleted file mode 100644 index efe67fcc0..000000000 --- a/packages/schema/src/generator/service/index.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { DataModel, isDataModel } from '@lang/generated/ast'; -import { camelCase } from 'change-case'; -import * as path from 'path'; -import { Project } from 'ts-morph'; -import { RUNTIME_PACKAGE } from '../constants'; -import { Context, Generator } from '../types'; - -/** - * Generates ZenStack service code - */ -export default class ServiceGenerator implements Generator { - get name() { - return 'service'; - } - - get startMessage() { - return 'Generating ZenStack service...'; - } - - get successMessage() { - return 'Successfully generated ZenStack service'; - } - - async generate(context: Context) { - const project = new Project(); - const sf = project.createSourceFile( - path.join(context.generatedCodeDir, 'src/index.ts'), - undefined, - { overwrite: true } - ); - - const models = context.schema.declarations.filter((d): d is DataModel => - isDataModel(d) - ); - - sf.addStatements([ - `import { Prisma as P, PrismaClient } from "../.prisma";`, - `import { DefaultService } from "${RUNTIME_PACKAGE}/lib/service";`, - `import { CRUD } from "${RUNTIME_PACKAGE}/lib/handler/data/crud";`, - `import type { QueryContext } from "${RUNTIME_PACKAGE}/lib/types";`, - `import type { ${models - .map((m) => m.name) - .join(', ')} } from "../.prisma";`, - ]); - - const cls = sf.addClass({ - name: 'ZenStackService', - isExported: true, - extends: 'DefaultService', - }); - - cls.addProperty({ - name: 'private crud', - initializer: `new CRUD(this)`, - }); - - cls.addMethod({ - name: 'initializePrisma', - }).setBodyText(` - const logConfig = (this.config.log || []) - .filter(item => typeof item === 'string' ? ['info', 'warn', 'error', 'query'].includes(item): ['info', 'warn', 'error', 'query'].includes(item.level)); - return new PrismaClient({log: logConfig as any }); - `); - - cls.addMethod({ - name: 'loadGuardModule', - isAsync: true, - }).setBodyText(` - return import('./query/guard'); - `); - - cls.addMethod({ - name: 'loadFieldConstraintModule', - isAsync: true, - }).setBodyText(` - return import('./field-constraint'); - `); - - // server-side CRUD operations per model - for (const model of models) { - cls.addGetAccessor({ - name: camelCase(model.name), - }).setBodyText(` - return { - get: (context: QueryContext, id: string, args?: P.SelectSubset>) => - this.crud.get('${model.name}', id, args, context) as Promise> | undefined>, - find: (context: QueryContext, args?: P.SelectSubset) => - this.crud.find('${model.name}', args, context) as Promise, Array>>>, - create: (context: QueryContext, args: P.${model.name}CreateArgs) => - this.crud.create('${model.name}', args, context) as Promise>>, - update: >(context: QueryContext, id: string, args: Omit) => - this.crud.update('${model.name}', id, args, context) as Promise>>, - del: >(context: QueryContext, id: string, args?: Omit) => - this.crud.del('${model.name}', id, args, context) as Promise>>, - } - `); - } - - // Recommended by Prisma for Next.js - // https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem - sf.addStatements([ - 'declare global { var zenstackService: ZenStackService | undefined}', - 'const service = global.zenstackService || new ZenStackService();', - 'export default service;', - `if (process.env.NODE_ENV !== 'production') global.zenstackService = service;`, - ]); - - sf.formatText(); - await project.save(); - - return []; - } -} diff --git a/packages/schema/src/generator/tsc/index.ts b/packages/schema/src/generator/tsc/index.ts deleted file mode 100644 index 713a5f7f2..000000000 --- a/packages/schema/src/generator/tsc/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import * as fs from 'fs'; -import path from 'path'; -import { execSync } from '../../utils/exec-utils'; -import { Context, Generator, GeneratorError } from '../types'; - -export class TypescriptCompilation implements Generator { - get name(): string { - return 'tsc'; - } - - get startMessage() { - return 'Transpiling generated code...'; - } - - get successMessage() { - return 'Successfully transpiled all generated code'; - } - - async generate(context: Context) { - // generate package.json - const packageJson = require(path.join( - __dirname, - '../res', - 'package.template.json' - )); - - fs.writeFileSync( - path.join(context.generatedCodeDir, 'package.json'), - JSON.stringify(packageJson, undefined, 4) - ); - - // compile ts sources - const tsConfig = require(path.join( - __dirname, - '../res', - 'tsconfig.template.json' - )); - fs.writeFileSync( - path.join(context.generatedCodeDir, 'tsconfig.json'), - JSON.stringify(tsConfig, undefined, 4) - ); - - try { - execSync( - `npx tsc -p "${path.join( - context.generatedCodeDir, - 'tsconfig.json' - )}"` - ); - } catch { - throw new GeneratorError( - 'Something went wrong, generated runtime code failed to compile...\nPlease check errors above.' - ); - } - - return []; - } -} diff --git a/packages/schema/src/language-server/constants.ts b/packages/schema/src/language-server/constants.ts index 62a0451cb..d6e7a27fd 100644 --- a/packages/schema/src/language-server/constants.ts +++ b/packages/schema/src/language-server/constants.ts @@ -1,27 +1,12 @@ /** * Supported Prisma db providers */ -export const SUPPORTED_PROVIDERS = [ - 'sqlite', - 'postgresql', - 'mysql', - 'sqlserver', - 'cockroachdb', -]; +export const SUPPORTED_PROVIDERS = ['sqlite', 'postgresql', 'mysql', 'sqlserver', 'cockroachdb']; /** * All scalar types */ -export const SCALAR_TYPES = [ - 'String', - 'Int', - 'Float', - 'Decimal', - 'BigInt', - 'Boolean', - 'Bytes', - 'DateTime', -]; +export const SCALAR_TYPES = ['String', 'Int', 'Float', 'Decimal', 'BigInt', 'Boolean', 'Bytes', 'DateTime']; /** * Name of standard library module diff --git a/packages/schema/src/language-server/langium-ext.d.ts b/packages/schema/src/language-server/langium-ext.d.ts deleted file mode 100644 index 84ffffebf..000000000 --- a/packages/schema/src/language-server/langium-ext.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ResolvedType } from '@lang/types'; -import { AttributeParam } from './generated/ast'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { AttributeArg } from './generated/ast'; - -declare module 'langium' { - export interface AstNode { - /** - * Resolved type information attached to expressions - */ - $resolvedType?: ResolvedType; - } -} - -declare module './generated/ast' { - interface AttributeArg { - /** - * Resolved attribute param declaration - */ - $resolvedParam?: AttributeParam; - } -} diff --git a/packages/schema/src/language-server/types.ts b/packages/schema/src/language-server/types.ts index 68bbfd499..b7420ba09 100644 --- a/packages/schema/src/language-server/types.ts +++ b/packages/schema/src/language-server/types.ts @@ -1,18 +1,4 @@ import { AstNode, ValidationAcceptor } from 'langium'; -import { AbstractDeclaration, ExpressionType } from './generated/ast'; - -/** - * Shape of type resolution result: an expression type or reference to a declaration - */ -export type ResolvedShape = ExpressionType | AbstractDeclaration; - -/** - * Resolved type information (attached to expressions by linker) - */ -export type ResolvedType = { - decl?: ResolvedShape; - array?: boolean; -}; /** * AST validator contract diff --git a/packages/schema/src/language-server/utils.ts b/packages/schema/src/language-server/utils.ts index c29936689..261c4815e 100644 --- a/packages/schema/src/language-server/utils.ts +++ b/packages/schema/src/language-server/utils.ts @@ -1,6 +1,6 @@ import { AstNode } from 'langium'; import { STD_LIB_MODULE_NAME } from './constants'; -import { isModel, Model } from './generated/ast'; +import { isModel, Model } from '@zenstackhq/language/ast'; /** * Gets the toplevel Model containing the given node. @@ -17,5 +17,5 @@ export function getContainingModel(node: AstNode | undefined): Model | null { */ export function isFromStdlib(node: AstNode) { const model = getContainingModel(node); - return model && model.$document?.uri.path.endsWith(STD_LIB_MODULE_NAME); + return !!model && !!model.$document && model.$document.uri.path.endsWith(STD_LIB_MODULE_NAME); } diff --git a/packages/schema/src/language-server/validator/attribute-validator.ts b/packages/schema/src/language-server/validator/attribute-validator.ts index dc81353ef..b13791207 100644 --- a/packages/schema/src/language-server/validator/attribute-validator.ts +++ b/packages/schema/src/language-server/validator/attribute-validator.ts @@ -1,5 +1,5 @@ -import { Attribute } from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; +import { Attribute } from '@zenstackhq/language/ast'; +import { AstValidator } from '../types'; import { ValidationAcceptor } from 'langium'; /** diff --git a/packages/schema/src/language-server/validator/datamodel-validator.ts b/packages/schema/src/language-server/validator/datamodel-validator.ts index d73946460..7c7742af9 100644 --- a/packages/schema/src/language-server/validator/datamodel-validator.ts +++ b/packages/schema/src/language-server/validator/datamodel-validator.ts @@ -1,4 +1,3 @@ -import { SCALAR_TYPES } from '@lang/constants'; import { ArrayExpr, Attribute, @@ -12,14 +11,13 @@ import { isDataModelField, isLiteralExpr, ReferenceExpr, -} from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; +} from '@zenstackhq/language/ast'; import { ValidationAcceptor } from 'langium'; -import { - assignableToAttributeParam, - validateDuplicatedDeclarations, -} from './utils'; import pluralize from 'pluralize'; +import { analyzePolicies } from '../../utils/ast-utils'; +import { SCALAR_TYPES } from '../constants'; +import { AstValidator } from '../types'; +import { assignableToAttributeParam, validateDuplicatedDeclarations } from './utils'; /** * Validates data model declarations. @@ -32,61 +30,39 @@ export default class DataModelValidator implements AstValidator { } private validateFields(dm: DataModel, accept: ValidationAcceptor) { - const idFields = dm.fields.filter((f) => - f.attributes.find((attr) => attr.decl.ref?.name === '@id') - ); + const idFields = dm.fields.filter((f) => f.attributes.find((attr) => attr.decl.ref?.name === '@id')); if (idFields.length === 0) { - accept('error', 'Model must include a field with @id attribute', { + const { allows, denies, hasFieldValidation } = analyzePolicies(dm); + if (allows.length > 0 || denies.length > 0 || hasFieldValidation) { + // TODO: relax this requirement to require only @unique fields + // when access policies or field valdaition is used, require an @id field + accept('error', 'Model must include a field with @id attribute', { + node: dm, + }); + } + } else if (idFields.length > 1) { + accept('error', 'Model can include at most one field with @id attribute', { node: dm, }); - } else if (idFields.length > 1) { - accept( - 'error', - 'Model can include at most one field with @id attribute', - { - node: dm, - } - ); } else { if (idFields[0].type.optional) { - accept( - 'error', - 'Field with @id attribute must not be optional', - { node: idFields[0] } - ); + accept('error', 'Field with @id attribute must not be optional', { node: idFields[0] }); } - if ( - idFields[0].type.array || - !idFields[0].type.type || - !SCALAR_TYPES.includes(idFields[0].type.type) - ) { - accept( - 'error', - 'Field with @id attribute must be of scalar type', - { node: idFields[0] } - ); + if (idFields[0].type.array || !idFields[0].type.type || !SCALAR_TYPES.includes(idFields[0].type.type)) { + accept('error', 'Field with @id attribute must be of scalar type', { node: idFields[0] }); } } dm.fields.forEach((field) => this.validateField(field, accept)); } - private validateField( - field: DataModelField, - accept: ValidationAcceptor - ): void { + private validateField(field: DataModelField, accept: ValidationAcceptor): void { if (field.type.array && field.type.optional) { - accept( - 'error', - 'Optional lists are not supported. Use either `Type[]` or `Type?`', - { node: field.type } - ); + accept('error', 'Optional lists are not supported. Use either `Type[]` or `Type?`', { node: field.type }); } - field.attributes.forEach((attr) => - this.validateAttributeApplication(attr, accept) - ); + field.attributes.forEach((attr) => this.validateAttributeApplication(attr, accept)); if (isDataModel(field.type.reference?.ref)) { this.validateRelationField(field, accept); @@ -110,23 +86,12 @@ export default class DataModelValidator implements AstValidator { const targetDecl = attr.$container; if (decl.name === '@@@targetField' && !isAttribute(targetDecl)) { - accept( - 'error', - `attribute "${decl.name}" can only be used on attribute declarations`, - { node: attr } - ); + accept('error', `attribute "${decl.name}" can only be used on attribute declarations`, { node: attr }); return; } - if ( - isDataModelField(targetDecl) && - !this.isValidAttributeTarget(decl, targetDecl) - ) { - accept( - 'error', - `attribute "${decl.name}" cannot be used on this type of field`, - { node: attr } - ); + if (isDataModelField(targetDecl) && !this.isValidAttributeTarget(decl, targetDecl)) { + accept('error', `attribute "${decl.name}" cannot be used on this type of field`, { node: attr }); } const filledParams = new Set(); @@ -134,9 +99,7 @@ export default class DataModelValidator implements AstValidator { for (const arg of attr.args) { let paramDecl: AttributeParam | undefined; if (!arg.name) { - paramDecl = decl.params.find( - (p) => p.default && !filledParams.has(p) - ); + paramDecl = decl.params.find((p) => p.default && !filledParams.has(p)); if (!paramDecl) { accept('error', `Unexpected unnamed argument`, { node: arg, @@ -146,13 +109,9 @@ export default class DataModelValidator implements AstValidator { } else { paramDecl = decl.params.find((p) => p.name === arg.name); if (!paramDecl) { - accept( - 'error', - `Attribute "${decl.name}" doesn't have a parameter named "${arg.name}"`, - { - node: arg, - } - ); + accept('error', `Attribute "${decl.name}" doesn't have a parameter named "${arg.name}"`, { + node: arg, + }); return false; } } @@ -165,27 +124,18 @@ export default class DataModelValidator implements AstValidator { } if (filledParams.has(paramDecl)) { - accept( - 'error', - `Parameter "${paramDecl.name}" is already provided`, - { node: arg } - ); + accept('error', `Parameter "${paramDecl.name}" is already provided`, { node: arg }); return false; } filledParams.add(paramDecl); arg.$resolvedParam = paramDecl; } - const missingParams = decl.params.filter( - (p) => !p.type.optional && !filledParams.has(p) - ); + const missingParams = decl.params.filter((p) => !p.type.optional && !filledParams.has(p)); if (missingParams.length > 0) { accept( 'error', - `Required ${pluralize( - 'parameter', - missingParams.length - )} not provided: ${missingParams + `Required ${pluralize('parameter', missingParams.length)} not provided: ${missingParams .map((p) => p.name) .join(', ')}`, { node: attr } @@ -196,13 +146,8 @@ export default class DataModelValidator implements AstValidator { return true; } - private isValidAttributeTarget( - attrDecl: Attribute, - targetDecl: DataModelField - ) { - const targetField = attrDecl.attributes.find( - (attr) => attr.decl.ref?.name === '@@@targetField' - ); + private isValidAttributeTarget(attrDecl: Attribute, targetDecl: DataModelField) { + const targetField = attrDecl.attributes.find((attr) => attr.decl.ref?.name === '@@@targetField'); if (!targetField) { // no field type constraint return true; @@ -240,8 +185,7 @@ export default class DataModelValidator implements AstValidator { allowed = allowed || targetDecl.type.type === 'Bytes'; break; case 'ModelField': - allowed = - allowed || isDataModel(targetDecl.type.reference?.ref); + allowed = allowed || isDataModel(targetDecl.type.reference?.ref); break; default: break; @@ -255,9 +199,7 @@ export default class DataModelValidator implements AstValidator { } private parseRelation(field: DataModelField, accept?: ValidationAcceptor) { - const relAttr = field.attributes.find( - (attr) => attr.decl.ref?.name === '@relation' - ); + const relAttr = field.attributes.find((attr) => attr.decl.ref?.name === '@relation'); let name: string | undefined; let fields: ReferenceExpr[] | undefined; @@ -296,13 +238,47 @@ export default class DataModelValidator implements AstValidator { } } + if (!fields || !references) { + if (accept) { + accept('error', `Both "fields" and "references" must be provided`, { node: relAttr }); + } + } else { + // validate "fields" and "references" typing consistency + if (fields.length !== references.length) { + if (accept) { + accept('error', `"references" and "fields" must have the same length`, { node: relAttr }); + } + } else { + for (let i = 0; i < fields.length; i++) { + if (!fields[i].$resolvedType) { + if (accept) { + accept('error', `field reference is unresolved`, { node: fields[i] }); + } + } + if (!references[i].$resolvedType) { + if (accept) { + accept('error', `field reference is unresolved`, { node: references[i] }); + } + } + + if ( + fields[i].$resolvedType?.decl !== references[i].$resolvedType?.decl || + fields[i].$resolvedType?.array !== references[i].$resolvedType?.array + ) { + if (accept) { + accept('error', `values of "references" and "fields" must have the same type`, { + node: relAttr, + }); + } + } + } + } + } + return { attr: relAttr, name, fields, references, valid }; } - private validateRelationField( - field: DataModelField, - accept: ValidationAcceptor - ) { + private validateRelationField(field: DataModelField, accept: ValidationAcceptor) { const thisRelation = this.parseRelation(field, accept); if (!thisRelation.valid) { return; @@ -311,9 +287,7 @@ export default class DataModelValidator implements AstValidator { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const oppositeModel = field.type.reference!.ref! as DataModel; - let oppositeFields = oppositeModel.fields.filter( - (f) => f.type.reference?.ref === field.$container - ); + let oppositeFields = oppositeModel.fields.filter((f) => f.type.reference?.ref === field.$container); oppositeFields = oppositeFields.filter((f) => { const fieldRel = this.parseRelation(f); return fieldRel.valid && fieldRel.name === thisRelation.name; @@ -330,13 +304,9 @@ export default class DataModelValidator implements AstValidator { oppositeFields.forEach((f) => accept( 'error', - `Fields ${oppositeFields - .map((f) => '"' + f.name + '"') - .join(', ')} on model "${ + `Fields ${oppositeFields.map((f) => '"' + f.name + '"').join(', ')} on model "${ oppositeModel.name - }" refer to the same relation to model "${ - field.$container.name - }"`, + }" refer to the same relation to model "${field.$container.name}"`, { node: f } ) ); @@ -350,25 +320,18 @@ export default class DataModelValidator implements AstValidator { if (thisRelation?.references?.length && thisRelation.fields?.length) { if (oppositeRelation?.references || oppositeRelation?.fields) { - accept( - 'error', - '"fields" and "references" must be provided only on one side of relation field', - { node: oppositeField } - ); + accept('error', '"fields" and "references" must be provided only on one side of relation field', { + node: oppositeField, + }); return; } else { relationOwner = oppositeField; } - } else if ( - oppositeRelation?.references?.length && - oppositeRelation.fields?.length - ) { + } else if (oppositeRelation?.references?.length && oppositeRelation.fields?.length) { if (thisRelation?.references || thisRelation?.fields) { - accept( - 'error', - '"fields" and "references" must be provided only on one side of relation field', - { node: field } - ); + accept('error', '"fields" and "references" must be provided only on one side of relation field', { + node: field, + }); return; } else { relationOwner = field; @@ -408,12 +371,7 @@ export default class DataModelValidator implements AstValidator { thisRelation.fields?.forEach((ref) => { const refField = ref.target.ref as DataModelField; - if ( - refField && - !refField.attributes.find( - (a) => a.decl.ref?.name === '@unique' - ) - ) { + if (refField && !refField.attributes.find((a) => a.decl.ref?.name === '@unique')) { accept( 'error', `Field "${refField.name}" is part of a one-to-one relation and must be marked as @unique`, diff --git a/packages/schema/src/language-server/validator/datasource-validator.ts b/packages/schema/src/language-server/validator/datasource-validator.ts index da52876a3..bbb08fa8e 100644 --- a/packages/schema/src/language-server/validator/datasource-validator.ts +++ b/packages/schema/src/language-server/validator/datasource-validator.ts @@ -1,15 +1,10 @@ -import { DataSource, isInvocationExpr } from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; +import { DataSource, isInvocationExpr } from '@zenstackhq/language/ast'; +import { AstValidator } from '../types'; import { ValidationAcceptor } from 'langium'; import { getStringLiteral, validateDuplicatedDeclarations } from './utils'; import { SUPPORTED_PROVIDERS } from '../constants'; -const supportedFields = [ - 'provider', - 'url', - 'shadowDatabaseUrl', - 'relationMode', -]; +const supportedFields = ['provider', 'url', 'shadowDatabaseUrl', 'relationMode']; /** * Validates data source declarations. @@ -48,9 +43,9 @@ export default class DataSourceValidator implements AstValidator { } else if (!SUPPORTED_PROVIDERS.includes(value)) { accept( 'error', - `Provider "${value}" is not supported. Choose from ${SUPPORTED_PROVIDERS.map( - (p) => '"' + p + '"' - ).join(' | ')}.`, + `Provider "${value}" is not supported. Choose from ${SUPPORTED_PROVIDERS.map((p) => '"' + p + '"').join( + ' | ' + )}.`, { node: provider.value } ); } @@ -70,18 +65,10 @@ export default class DataSourceValidator implements AstValidator { continue; } const value = getStringLiteral(field.value); - if ( - !value && - !( - isInvocationExpr(field.value) && - field.value.function.ref?.name === 'env' - ) - ) { - accept( - 'error', - `"${fieldName}" must be set to a string literal or an invocation of "env" function`, - { node: field.value } - ); + if (!value && !(isInvocationExpr(field.value) && field.value.function.ref?.name === 'env')) { + accept('error', `"${fieldName}" must be set to a string literal or an invocation of "env" function`, { + node: field.value, + }); } } } @@ -91,11 +78,7 @@ export default class DataSourceValidator implements AstValidator { if (field) { const val = getStringLiteral(field.value); if (!val || !['foreignKeys', 'prisma'].includes(val)) { - accept( - 'error', - '"relationMode" must be set to "foreignKeys" or "prisma"', - { node: field.value } - ); + accept('error', '"relationMode" must be set to "foreignKeys" or "prisma"', { node: field.value }); } } } diff --git a/packages/schema/src/language-server/validator/enum-validator.ts b/packages/schema/src/language-server/validator/enum-validator.ts index a632097b9..ecf0828ed 100644 --- a/packages/schema/src/language-server/validator/enum-validator.ts +++ b/packages/schema/src/language-server/validator/enum-validator.ts @@ -1,5 +1,5 @@ -import { Enum } from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; +import { Enum } from '@zenstackhq/language/ast'; +import { AstValidator } from '../types'; import { ValidationAcceptor } from 'langium'; import { validateDuplicatedDeclarations } from './utils'; diff --git a/packages/schema/src/language-server/validator/expression-validator.ts b/packages/schema/src/language-server/validator/expression-validator.ts index fe52a8084..2f9ca1cf2 100644 --- a/packages/schema/src/language-server/validator/expression-validator.ts +++ b/packages/schema/src/language-server/validator/expression-validator.ts @@ -1,11 +1,7 @@ -import { - Expression, - isBinaryExpr, - isInvocationExpr, -} from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; -import { isFromStdlib } from '@lang/utils'; +import { Expression, isBinaryExpr } from '@zenstackhq/language/ast'; import { ValidationAcceptor } from 'langium'; +import { isAuthInvocation } from '../../utils/ast-utils'; +import { AstValidator } from '../types'; /** * Validates expressions. @@ -13,19 +9,11 @@ import { ValidationAcceptor } from 'langium'; export default class ExpressionValidator implements AstValidator { validate(expr: Expression, accept: ValidationAcceptor): void { if (!expr.$resolvedType) { - if (this.isAuthInvocation(expr)) { + if (isAuthInvocation(expr)) { // check was done at link time - accept( - 'error', - 'auth() cannot be resolved because no "User" model is defined', - { node: expr } - ); + accept('error', 'auth() cannot be resolved because no "User" model is defined', { node: expr }); } else if (this.isCollectionPredicate(expr)) { - accept( - 'error', - 'collection predicate can only be used on an array of model type', - { node: expr } - ); + accept('error', 'collection predicate can only be used on an array of model type', { node: expr }); } else { accept('error', 'expression cannot be resolved', { node: expr, @@ -37,12 +25,4 @@ export default class ExpressionValidator implements AstValidator { private isCollectionPredicate(expr: Expression) { return isBinaryExpr(expr) && ['?', '!', '^'].includes(expr.operator); } - - private isAuthInvocation(expr: Expression) { - return ( - isInvocationExpr(expr) && - expr.function.ref?.name === 'auth' && - isFromStdlib(expr.function.ref) - ); - } } diff --git a/packages/schema/src/language-server/validator/schema-validator.ts b/packages/schema/src/language-server/validator/schema-validator.ts index c3de7b3a6..e02ed68e3 100644 --- a/packages/schema/src/language-server/validator/schema-validator.ts +++ b/packages/schema/src/language-server/validator/schema-validator.ts @@ -1,6 +1,6 @@ -import { STD_LIB_MODULE_NAME } from '@lang/constants'; -import { isDataSource, Model } from '@lang/generated/ast'; -import { AstValidator } from '@lang/types'; +import { STD_LIB_MODULE_NAME } from '../constants'; +import { isDataSource, Model } from '@zenstackhq/language/ast'; +import { AstValidator } from '../types'; import { ValidationAcceptor } from 'langium'; import { validateDuplicatedDeclarations } from './utils'; @@ -21,11 +21,7 @@ export default class SchemaValidator implements AstValidator { if (dataSources.length === 0) { accept('error', 'Model must define a datasource', { node: model }); } else if (dataSources.length > 1) { - accept( - 'error', - 'Multiple datasource declarations are not allowed', - { node: dataSources[1] } - ); + accept('error', 'Multiple datasource declarations are not allowed', { node: dataSources[1] }); } } } diff --git a/packages/schema/src/language-server/validator/utils.ts b/packages/schema/src/language-server/validator/utils.ts index 2342fba06..b15de1e91 100644 --- a/packages/schema/src/language-server/validator/utils.ts +++ b/packages/schema/src/language-server/validator/utils.ts @@ -9,7 +9,7 @@ import { isDataModelField, isLiteralExpr, isReferenceExpr, -} from '@lang/generated/ast'; +} from '@zenstackhq/language/ast'; import { AstNode, ValidationAcceptor } from 'langium'; /** @@ -19,9 +19,7 @@ export function validateDuplicatedDeclarations( decls: Array, accept: ValidationAcceptor ): void { - const groupByName = decls.reduce< - Record> - >((group, decl) => { + const groupByName = decls.reduce>>((group, decl) => { group[decl.name] = group[decl.name] ?? []; group[decl.name].push(decl); return group; @@ -39,7 +37,7 @@ export function validateDuplicatedDeclarations( /** * Try getting string value from a potential string literal expression */ -export function getStringLiteral(node: AstNode): string | undefined { +export function getStringLiteral(node: AstNode | undefined): string | undefined { if (isLiteralExpr(node) && typeof node.value === 'string') { return node.value; } else { @@ -50,19 +48,12 @@ export function getStringLiteral(node: AstNode): string | undefined { /** * Determines if the given sourceType is assignable to a destination of destType */ -export function typeAssignable( - destType: ExpressionType, - sourceType: ExpressionType -): boolean { +export function typeAssignable(destType: ExpressionType, sourceType: ExpressionType): boolean { switch (destType) { case 'Any': return true; case 'Float': - return ( - sourceType === 'Any' || - sourceType === 'Int' || - sourceType === 'Float' - ); + return sourceType === 'Any' || sourceType === 'Int' || sourceType === 'Float'; default: return sourceType === 'Any' || sourceType === destType; } @@ -71,9 +62,7 @@ export function typeAssignable( /** * Maps a ZModel builtin type to expression type */ -export function mapBuiltinTypeToExpressionType( - type: BuiltinType | 'Any' | 'Null' -): ExpressionType | 'Any' { +export function mapBuiltinTypeToExpressionType(type: BuiltinType | 'Any' | 'Null'): ExpressionType | 'Any' { switch (type) { case 'Any': case 'Boolean': @@ -116,43 +105,30 @@ export function assignableToAttributeParam( return false; } - if (dstType === 'FieldReference') { + if (dstType === 'FieldReference' || dstType === 'TransitiveFieldReference') { if (dstIsArray) { return ( isArrayExpr(arg.value) && - !arg.value.items.find( - (item) => - !isReferenceExpr(item) || - !isDataModelField(item.target.ref) - ) + !arg.value.items.find((item) => !isReferenceExpr(item) || !isDataModelField(item.target.ref)) ); } else { - return ( - isReferenceExpr(arg.value) && - isDataModelField(arg.value.target.ref) - ); + return isReferenceExpr(arg.value) && isDataModelField(arg.value.target.ref); } } else if (dstType === 'ContextType') { if (isDataModelField(attr.$container)) { if (!attr.$container?.type?.type) { return false; } - dstType = mapBuiltinTypeToExpressionType( - attr.$container.type.type - ); + dstType = mapBuiltinTypeToExpressionType(attr.$container.type.type); } else { dstType = 'Any'; } } return ( - typeAssignable(dstType, argResolvedType.decl) && - dstIsArray === argResolvedType.array + typeAssignable(dstType, argResolvedType.decl) && (dstType === 'Any' || dstIsArray === argResolvedType.array) ); } else { - return ( - dstRef?.ref === argResolvedType.decl && - dstIsArray === argResolvedType.array - ); + return dstRef?.ref === argResolvedType.decl && dstIsArray === argResolvedType.array; } } diff --git a/packages/schema/src/language-server/validator/zmodel-validator.ts b/packages/schema/src/language-server/validator/zmodel-validator.ts index 41fc8a063..7f5630b61 100644 --- a/packages/schema/src/language-server/validator/zmodel-validator.ts +++ b/packages/schema/src/language-server/validator/zmodel-validator.ts @@ -1,19 +1,5 @@ -import { - AstNode, - LangiumDocument, - ValidationAcceptor, - ValidationChecks, - ValidationRegistry, -} from 'langium'; -import { - Attribute, - DataModel, - DataSource, - Enum, - Expression, - Model, - ZModelAstType, -} from '../generated/ast'; +import { AstNode, LangiumDocument, ValidationAcceptor, ValidationChecks, ValidationRegistry } from 'langium'; +import { Attribute, DataModel, DataSource, Enum, Expression, Model, ZModelAstType } from '@zenstackhq/language/ast'; import type { ZModelServices } from '../zmodel-module'; import SchemaValidator from './schema-validator'; import DataSourceValidator from './datasource-validator'; @@ -56,10 +42,7 @@ export class ZModelValidator { currNode = currNode.$container; } - return ( - doc?.parseResult.lexerErrors.length === 0 && - doc?.parseResult.parserErrors.length === 0 - ); + return doc?.parseResult.lexerErrors.length === 0 && doc?.parseResult.parserErrors.length === 0; } checkModel(node: Model, accept: ValidationAcceptor): void { @@ -67,13 +50,11 @@ export class ZModelValidator { } checkDataSource(node: DataSource, accept: ValidationAcceptor): void { - this.shouldCheck(node) && - new DataSourceValidator().validate(node, accept); + this.shouldCheck(node) && new DataSourceValidator().validate(node, accept); } checkDataModel(node: DataModel, accept: ValidationAcceptor): void { - this.shouldCheck(node) && - new DataModelValidator().validate(node, accept); + this.shouldCheck(node) && new DataModelValidator().validate(node, accept); } checkEnum(node: Enum, accept: ValidationAcceptor): void { @@ -81,8 +62,7 @@ export class ZModelValidator { } checkAttribute(node: Attribute, accept: ValidationAcceptor): void { - this.shouldCheck(node) && - new AttributeValidator().validate(node, accept); + this.shouldCheck(node) && new AttributeValidator().validate(node, accept); } checkExpression(node: Expression, accept: ValidationAcceptor): void { diff --git a/packages/schema/src/language-server/zmodel-formatter.ts b/packages/schema/src/language-server/zmodel-formatter.ts new file mode 100644 index 000000000..bf14e7268 --- /dev/null +++ b/packages/schema/src/language-server/zmodel-formatter.ts @@ -0,0 +1,20 @@ +import { AbstractFormatter, AstNode, Formatting } from 'langium'; + +import * as ast from '@zenstackhq/language/ast'; + +export class ZModelFormatter extends AbstractFormatter { + protected format(node: AstNode): void { + const formatter = this.getNodeFormatter(node); + if (ast.isAbstractDeclaration(node)) { + const bracesOpen = formatter.keyword('{'); + const bracesClose = formatter.keyword('}'); + formatter.interior(bracesOpen, bracesClose).prepend(Formatting.indent()); + bracesOpen.prepend(Formatting.oneSpace()); + bracesClose.prepend(Formatting.newLine()); + } else if (ast.isModel(node)) { + const model = node as ast.Model; + const nodes = formatter.nodes(...model.declarations); + nodes.prepend(Formatting.noIndent()); + } + } +} diff --git a/packages/schema/src/language-server/zmodel-linker.ts b/packages/schema/src/language-server/zmodel-linker.ts index 2dcaf6c81..83d3c7710 100644 --- a/packages/schema/src/language-server/zmodel-linker.ts +++ b/packages/schema/src/language-server/zmodel-linker.ts @@ -1,40 +1,45 @@ -import { - AstNode, - AstNodeDescription, - AstNodeDescriptionProvider, - DefaultLinker, - DocumentState, - interruptAndCheck, - isReference, - LangiumDocument, - LangiumServices, - LinkingError, - Reference, - streamContents, -} from 'langium'; -import { CancellationToken } from 'vscode-jsonrpc'; import { ArrayExpr, AttributeArg, + AttributeParam, BinaryExpr, DataModel, DataModelField, DataModelFieldType, EnumField, - Function, + Expression, + FunctionDecl, FunctionParam, FunctionParamType, InvocationExpr, - isDataModel, LiteralExpr, MemberAccessExpr, NullExpr, ReferenceExpr, ReferenceTarget, + ResolvedShape, ThisExpr, UnaryExpr, -} from './generated/ast'; -import { ResolvedShape } from './types'; + isArrayExpr, + isDataModel, + isDataModelField, + isReferenceExpr, +} from '@zenstackhq/language/ast'; +import { + AstNode, + AstNodeDescription, + AstNodeDescriptionProvider, + DefaultLinker, + DocumentState, + LangiumDocument, + LangiumServices, + LinkingError, + Reference, + interruptAndCheck, + isReference, + streamContents, +} from 'langium'; +import { CancellationToken } from 'vscode-jsonrpc'; import { getContainingModel, isFromStdlib } from './utils'; import { mapBuiltinTypeToExpressionType } from './validator/utils'; @@ -58,14 +63,8 @@ export class ZModelLinker extends DefaultLinker { //#region Reference linking - async link( - document: LangiumDocument, - cancelToken = CancellationToken.None - ): Promise { - if ( - document.parseResult.lexerErrors?.length > 0 || - document.parseResult.parserErrors?.length > 0 - ) { + async link(document: LangiumDocument, cancelToken = CancellationToken.None): Promise { + if (document.parseResult.lexerErrors?.length > 0 || document.parseResult.parserErrors?.length > 0) { return; } @@ -82,14 +81,7 @@ export class ZModelLinker extends DefaultLinker { document: LangiumDocument, extraScopes: ScopeProvider[] ) { - if ( - !this.resolveFromScopeProviders( - container, - property, - document, - extraScopes - ) - ) { + if (!this.resolveFromScopeProviders(container, property, document, extraScopes)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const reference: Reference = (container as any)[property]; this.doLink({ reference, container, property }, document); @@ -112,34 +104,21 @@ export class ZModelLinker extends DefaultLinker { const target = provider(reference.$refText); if (target) { reference._ref = target; - reference._nodeDescription = - this.descriptions.createDescription( - target, - target.name, - document - ); + reference._nodeDescription = this.descriptions.createDescription(target, target.name, document); return target; } } return null; } - private resolve( - node: AstNode, - document: LangiumDocument, - extraScopes: ScopeProvider[] = [] - ) { + private resolve(node: AstNode, document: LangiumDocument, extraScopes: ScopeProvider[] = []) { switch (node.$type) { case LiteralExpr: this.resolveLiteral(node as LiteralExpr); break; case InvocationExpr: - this.resolveInvocation( - node as InvocationExpr, - document, - extraScopes - ); + this.resolveInvocation(node as InvocationExpr, document, extraScopes); break; case ArrayExpr: @@ -147,19 +126,11 @@ export class ZModelLinker extends DefaultLinker { break; case ReferenceExpr: - this.resolveReference( - node as ReferenceExpr, - document, - extraScopes - ); + this.resolveReference(node as ReferenceExpr, document, extraScopes); break; case MemberAccessExpr: - this.resolveMemberAccess( - node as MemberAccessExpr, - document, - extraScopes - ); + this.resolveMemberAccess(node as MemberAccessExpr, document, extraScopes); break; case UnaryExpr: @@ -179,11 +150,7 @@ export class ZModelLinker extends DefaultLinker { break; case AttributeArg: - this.resolveAttributeArg( - node as AttributeArg, - document, - extraScopes - ); + this.resolveAttributeArg(node as AttributeArg, document, extraScopes); break; default: @@ -192,11 +159,7 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveBinary( - node: BinaryExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveBinary(node: BinaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { switch (node.operator) { // TODO: support arithmetics? // case '+': @@ -232,44 +195,26 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveUnary( - node: UnaryExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveUnary(node: UnaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { this.resolve(node.operand, document, extraScopes); node.$resolvedType = node.operand.$resolvedType; } - private resolveReference( - node: ReferenceExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveReference(node: ReferenceExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { this.linkReference(node, 'target', document, extraScopes); node.args.forEach((arg) => this.resolve(arg, document, extraScopes)); if (node.target.ref) { // resolve type if (node.target.ref.$type === EnumField) { - this.resolveToBuiltinTypeOrDecl( - node, - node.target.ref.$container - ); + this.resolveToBuiltinTypeOrDecl(node, node.target.ref.$container); } else { - this.resolveToDeclaredType( - node, - (node.target.ref as DataModelField | FunctionParam).type - ); + this.resolveToDeclaredType(node, (node.target.ref as DataModelField | FunctionParam).type); } } } - private resolveArray( - node: ArrayExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveArray(node: ArrayExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { node.items.forEach((item) => this.resolve(item, document, extraScopes)); const itemType = node.items[0].$resolvedType; @@ -278,31 +223,39 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveInvocation( - node: InvocationExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveInvocation(node: InvocationExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { this.linkReference(node, 'function', document, extraScopes); node.args.forEach((arg) => this.resolve(arg, document, extraScopes)); if (node.function.ref) { // eslint-disable-next-line @typescript-eslint/ban-types - const funcDecl = node.function.ref as Function; + const funcDecl = node.function.ref as FunctionDecl; if (funcDecl.name === 'auth' && isFromStdlib(funcDecl)) { // auth() function is resolved to User model in the current document const model = getContainingModel(node); - const userModel = model?.declarations.find( - (d) => isDataModel(d) && d.name === 'User' - ); + const userModel = model?.declarations.find((d) => isDataModel(d) && d.name === 'User'); if (userModel) { node.$resolvedType = { decl: userModel }; } + } else if (funcDecl.name === 'future' && isFromStdlib(funcDecl)) { + // future() function is resolved to current model + node.$resolvedType = { decl: this.getContainingDataModel(node) }; } else { this.resolveToDeclaredType(node, funcDecl.returnType); } } } + private getContainingDataModel(node: Expression): DataModel | undefined { + let curr: AstNode | undefined = node.$container; + while (curr) { + if (isDataModel(curr)) { + return curr; + } + curr = curr.$container; + } + return undefined; + } + private resolveLiteral(node: LiteralExpr) { const type = typeof node.value === 'string' @@ -326,14 +279,9 @@ export class ZModelLinker extends DefaultLinker { this.resolve(node.operand, document, extraScopes); const operandResolved = node.operand.$resolvedType; - if ( - operandResolved && - !operandResolved.array && - isDataModel(operandResolved.decl) - ) { + if (operandResolved && !operandResolved.array && isDataModel(operandResolved.decl)) { const modelDecl = operandResolved.decl as DataModel; - const provider = (name: string) => - modelDecl.fields.find((f) => f.name === name); + const provider = (name: string) => modelDecl.fields.find((f) => f.name === name); extraScopes = [provider, ...extraScopes]; } @@ -343,22 +291,13 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveCollectionPredicate( - node: BinaryExpr, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private resolveCollectionPredicate(node: BinaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[]) { this.resolve(node.left, document, extraScopes); const resolvedType = node.left.$resolvedType; - if ( - resolvedType && - isDataModel(resolvedType.decl) && - resolvedType.array - ) { + if (resolvedType && isDataModel(resolvedType.decl) && resolvedType.array) { const dataModelDecl = resolvedType.decl; - const provider = (name: string) => - dataModelDecl.fields.find((f) => f.name === name); + const provider = (name: string) => dataModelDecl.fields.find((f) => f.name === name); extraScopes = [provider, ...extraScopes]; this.resolve(node.right, document, extraScopes); this.resolveToBuiltinTypeOrDecl(node, 'Boolean'); @@ -396,20 +335,86 @@ export class ZModelLinker extends DefaultLinker { this.resolveToBuiltinTypeOrDecl(node, 'Null'); } - private resolveAttributeArg( - node: AttributeArg, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { - this.resolve(node.value, document, extraScopes); + private resolveAttributeArg(node: AttributeArg, document: LangiumDocument, extraScopes: ScopeProvider[]) { + const attrParam = this.findAttrParamForArg(node); + const attrAppliedOn = node.$container.$container; + + if (attrParam?.type.type === 'TransitiveFieldReference' && isDataModelField(attrAppliedOn)) { + // "TransitiveFieldReference" is resolved in the context of the containing model of the field + // where the attribute is applied + // + // E.g.: + // + // model A { + // myId @id String + // } + // + // model B { + // id @id String + // a A @relation(fields: [id], references: [myId]) + // } + // + // In model B, the attribute argument "myId" is resolved to the field "myId" in model A + + const transtiveDataModel = attrAppliedOn.type.reference?.ref as DataModel; + if (transtiveDataModel) { + // resolve references in the context of the transitive data model + const scopeProvider = (name: string) => transtiveDataModel.fields.find((f) => f.name === name); + if (isArrayExpr(node.value)) { + node.value.items.forEach((item) => { + if (isReferenceExpr(item)) { + const resolved = this.resolveFromScopeProviders(item, 'target', document, [scopeProvider]); + if (resolved) { + this.resolveToDeclaredType(item, (resolved as DataModelField).type); + } else { + // need to clear linked reference, because it's resolved in default scope by default + const ref = item.target as DefaultReference; + ref._ref = this.createLinkingError({ + reference: ref, + container: item, + property: 'target', + }); + } + } + }); + if (node.value.items[0]?.$resolvedType?.decl) { + this.resolveToBuiltinTypeOrDecl(node.value, node.value.items[0].$resolvedType.decl, true); + } + } else if (isReferenceExpr(node.value)) { + const resolved = this.resolveFromScopeProviders(node.value, 'target', document, [scopeProvider]); + if (resolved) { + this.resolveToDeclaredType(node.value, (resolved as DataModelField).type); + } else { + // need to clear linked reference, because it's resolved in default scope by default + const ref = node.value.target as DefaultReference; + ref._ref = this.createLinkingError({ + reference: ref, + container: node.value, + property: 'target', + }); + } + } + } + } else { + this.resolve(node.value, document, extraScopes); + } node.$resolvedType = node.value.$resolvedType; } - private resolveDefault( - node: AstNode, - document: LangiumDocument, - extraScopes: ScopeProvider[] - ) { + private findAttrParamForArg(arg: AttributeArg): AttributeParam | undefined { + const attr = arg.$container.decl.ref; + if (!attr) { + return undefined; + } + if (arg.name) { + return attr.params?.find((p) => p.name === arg.name); + } else { + const index = arg.$container.args.findIndex((a) => a === arg); + return attr.params[index]; + } + } + + private resolveDefault(node: AstNode, document: LangiumDocument, extraScopes: ScopeProvider[]) { for (const [property, value] of Object.entries(node)) { if (!property.startsWith('$')) { if (isReference(value)) { @@ -426,10 +431,7 @@ export class ZModelLinker extends DefaultLinker { //#region Utils - private resolveToDeclaredType( - node: AstNode, - type: FunctionParamType | DataModelFieldType - ) { + private resolveToDeclaredType(node: AstNode, type: FunctionParamType | DataModelFieldType) { if (type.type) { const mappedType = mapBuiltinTypeToExpressionType(type.type); node.$resolvedType = { decl: mappedType, array: type.array }; @@ -441,11 +443,7 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveToBuiltinTypeOrDecl( - node: AstNode, - type: ResolvedShape, - array = false - ) { + private resolveToBuiltinTypeOrDecl(node: AstNode, type: ResolvedShape, array = false) { node.$resolvedType = { decl: type, array }; } diff --git a/packages/schema/src/language-server/zmodel-module.ts b/packages/schema/src/language-server/zmodel-module.ts index 0a4091750..5a3507789 100644 --- a/packages/schema/src/language-server/zmodel-module.ts +++ b/packages/schema/src/language-server/zmodel-module.ts @@ -1,3 +1,4 @@ +import { ZModelGeneratedModule, ZModelGeneratedSharedModule } from '@zenstackhq/language/module'; import { createDefaultModule, DefaultConfigurationProvider, @@ -8,7 +9,6 @@ import { DefaultLanguageServer, DefaultServiceRegistry, DefaultSharedModuleContext, - DefaultTextDocumentFactory, inject, LangiumDefaultSharedServices, LangiumServices, @@ -17,18 +17,12 @@ import { MutexLock, PartialLangiumServices, } from 'langium'; -import { - ZModelGeneratedModule, - ZModelGeneratedSharedModule, -} from './generated/module'; -import { ZModelLinker } from './zmodel-linker'; -import { ZModelScopeComputation } from './zmodel-scope'; -import { - ZModelValidationRegistry, - ZModelValidator, -} from './validator/zmodel-validator'; import { TextDocuments } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; +import { ZModelValidationRegistry, ZModelValidator } from './validator/zmodel-validator'; +import { ZModelFormatter } from './zmodel-formatter'; +import { ZModelLinker } from './zmodel-linker'; +import { ZModelScopeComputation } from './zmodel-scope'; import ZModelWorkspaceManager from './zmodel-workspace-manager'; /** @@ -51,19 +45,18 @@ export type ZModelServices = LangiumServices & ZModelAddedServices; * declared custom services. The Langium defaults can be partially specified to override only * selected services, while the custom services must be fully specified. */ -export const ZModelModule: Module< - ZModelServices, - PartialLangiumServices & ZModelAddedServices -> = { +export const ZModelModule: Module = { references: { ScopeComputation: (services) => new ZModelScopeComputation(services), Linker: (services) => new ZModelLinker(services), }, validation: { - ValidationRegistry: (services) => - new ZModelValidationRegistry(services), + ValidationRegistry: (services) => new ZModelValidationRegistry(services), ZModelValidator: () => new ZModelValidator(), }, + lsp: { + Formatter: () => new ZModelFormatter(), + }, }; // this duplicates createDefaultSharedModule except that a custom WorkspaceManager is used @@ -77,22 +70,15 @@ export function createSharedModule( LanguageServer: (services) => new DefaultLanguageServer(services), }, workspace: { - LangiumDocuments: (services) => - new DefaultLangiumDocuments(services), - LangiumDocumentFactory: (services) => - new DefaultLangiumDocumentFactory(services), + LangiumDocuments: (services) => new DefaultLangiumDocuments(services), + LangiumDocumentFactory: (services) => new DefaultLangiumDocumentFactory(services), DocumentBuilder: (services) => new DefaultDocumentBuilder(services), TextDocuments: () => new TextDocuments(TextDocument), - TextDocumentFactory: (services) => - new DefaultTextDocumentFactory(services), IndexManager: (services) => new DefaultIndexManager(services), - WorkspaceManager: (services) => - new ZModelWorkspaceManager(services), - FileSystemProvider: (services) => - context.fileSystemProvider(services), + WorkspaceManager: (services) => new ZModelWorkspaceManager(services), + FileSystemProvider: (services) => context.fileSystemProvider(services), MutexLock: () => new MutexLock(), - ConfigurationProvider: (services) => - new DefaultConfigurationProvider(services), + ConfigurationProvider: (services) => new DefaultConfigurationProvider(services), }, }; } @@ -116,16 +102,9 @@ export function createZModelServices(context: DefaultSharedModuleContext): { shared: LangiumSharedServices; ZModel: ZModelServices; } { - const shared = inject( - createSharedModule(context), - ZModelGeneratedSharedModule - ); + const shared = inject(createSharedModule(context), ZModelGeneratedSharedModule); - const ZModel = inject( - createDefaultModule({ shared }), - ZModelGeneratedModule, - ZModelModule - ); + const ZModel = inject(createDefaultModule({ shared }), ZModelGeneratedModule, ZModelModule); shared.ServiceRegistry.register(ZModel); return { shared, ZModel }; } diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index ef716c6cc..51c7ae184 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -1,3 +1,4 @@ +import { isEnumField } from '@zenstackhq/language/ast'; import { AstNode, AstNodeDescription, @@ -8,7 +9,6 @@ import { streamAllContents, } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; -import { isEnumField } from './generated/ast'; /** * Custom Langium ScopeComputation implementation which adds enum fields into global scope @@ -30,12 +30,11 @@ export class ZModelScopeComputation extends DefaultScopeComputation { await interruptAndCheck(cancelToken); } if (isEnumField(node)) { - const desc = - this.services.workspace.AstNodeDescriptionProvider.createDescription( - node, - node.name, - document - ); + const desc = this.services.workspace.AstNodeDescriptionProvider.createDescription( + node, + node.name, + document + ); result.push(desc); } } diff --git a/packages/schema/src/language-server/zmodel-workspace-manager.ts b/packages/schema/src/language-server/zmodel-workspace-manager.ts index a5f6a5307..b4146cfd0 100644 --- a/packages/schema/src/language-server/zmodel-workspace-manager.ts +++ b/packages/schema/src/language-server/zmodel-workspace-manager.ts @@ -13,9 +13,7 @@ export default class ZModelWorkspaceManager extends DefaultWorkspaceManager { _collector: (document: LangiumDocument) => void ): Promise { await super.loadAdditionalDocuments(_folders, _collector); - const stdLibUri = URI.file( - path.join(__dirname, '../res', STD_LIB_MODULE_NAME) - ); + const stdLibUri = URI.file(path.join(__dirname, '../res', STD_LIB_MODULE_NAME)); console.log(`Adding stdlib document from ${stdLibUri}`); const stdlib = this.langiumDocuments.getOrCreateDocument(stdLibUri); _collector(stdlib); diff --git a/packages/schema/src/generator/prisma/expression-writer.ts b/packages/schema/src/plugins/access-policy/expression-writer.ts similarity index 51% rename from packages/schema/src/generator/prisma/expression-writer.ts rename to packages/schema/src/plugins/access-policy/expression-writer.ts index bdfcf08f7..e2449326f 100644 --- a/packages/schema/src/generator/prisma/expression-writer.ts +++ b/packages/schema/src/plugins/access-policy/expression-writer.ts @@ -1,5 +1,6 @@ import { BinaryExpr, + DataModel, Expression, isDataModel, isDataModelField, @@ -11,85 +12,94 @@ import { MemberAccessExpr, ReferenceExpr, UnaryExpr, -} from '@lang/generated/ast'; +} from '@zenstackhq/language/ast'; +import { GUARD_FIELD_NAME, PluginError } from '@zenstackhq/sdk'; import { CodeBlockWriter } from 'ts-morph'; -import { GUARD_FIELD_NAME } from '../constants'; -import { GeneratorError } from '../types'; +import { getIdField, isAuthInvocation } from '../../utils/ast-utils'; import TypeScriptExpressionTransformer from './typescript-expression-transformer'; +import { isFutureExpr } from './utils'; type ComparisonOperator = '==' | '!=' | '>' | '>=' | '<' | '<='; /** * Utility for writing ZModel expression as Prisma query argument objects into a ts-morph writer */ -export default class ExpressionWriter { - private readonly plainExprBuilder = new TypeScriptExpressionTransformer(); +export class ExpressionWriter { + private readonly plainExprBuilder: TypeScriptExpressionTransformer; - constructor(private readonly writer: CodeBlockWriter) {} + /** + * Constructs a new ExpressionWriter + * + * @param isPostGuard indicates if we're writing for post-update conditions + */ + constructor(private readonly writer: CodeBlockWriter, private readonly isPostGuard = false) { + this.plainExprBuilder = new TypeScriptExpressionTransformer(this.isPostGuard); + } /** * Writes the given ZModel expression. */ write(expr: Expression): void { - const _write = () => { - switch (expr.$type) { - case LiteralExpr: - this.writeLiteral(expr as LiteralExpr); - break; - - case UnaryExpr: - this.writeUnary(expr as UnaryExpr); - break; - - case BinaryExpr: - this.writeBinary(expr as BinaryExpr); - break; - - case ReferenceExpr: - this.writeReference(expr as ReferenceExpr); - break; - - case MemberAccessExpr: - this.writeMemberAccess(expr as MemberAccessExpr); - break; - - default: - throw new Error(`Not implemented: ${expr.$type}`); - } - }; + switch (expr.$type) { + case LiteralExpr: + this.writeLiteral(expr as LiteralExpr); + break; + + case UnaryExpr: + this.writeUnary(expr as UnaryExpr); + break; + + case BinaryExpr: + this.writeBinary(expr as BinaryExpr); + break; + + case ReferenceExpr: + this.writeReference(expr as ReferenceExpr); + break; + + case MemberAccessExpr: + this.writeMemberAccess(expr as MemberAccessExpr); + break; - this.block(_write); + default: + throw new Error(`Not implemented: ${expr.$type}`); + } } private writeReference(expr: ReferenceExpr) { if (isEnumField(expr.target.ref)) { throw new Error('We should never get here'); } else { - this.writer.write(`${expr.target.ref?.name}: true`); + this.block(() => { + this.writer.write(`${expr.target.ref?.name}: true`); + }); } } private writeMemberAccess(expr: MemberAccessExpr) { - this.writeFieldCondition( - expr.operand, - () => { - this.block(() => { - this.writer.write(`${expr.member.ref?.name}: true`); - }); - }, - 'is' - ); + this.block(() => { + // must be a boolean member + this.writeFieldCondition( + expr.operand, + () => { + this.block(() => { + this.writer.write(`${expr.member.ref?.name}: true`); + }); + }, + 'is' + ); + }); } private writeExprList(exprs: Expression[]) { - this.writer.writeLine('['); + this.writer.write('['); for (let i = 0; i < exprs.length; i++) { this.write(exprs[i]); if (i !== exprs.length - 1) { - this.writer.writeLine(','); + this.writer.write(','); } } - this.writer.writeLine(']'); + this.writer.write(']'); } private writeBinary(expr: BinaryExpr) { @@ -117,23 +127,31 @@ export default class ExpressionWriter { } private writeCollectionPredicate(expr: BinaryExpr, operator: string) { - this.writeFieldCondition( - expr.left, - () => { - this.write(expr.right); - }, - operator === '?' ? 'some' : operator === '!' ? 'every' : 'none' - ); + this.block(() => { + this.writeFieldCondition( + expr.left, + () => { + this.write(expr.right); + }, + operator === '?' ? 'some' : operator === '!' ? 'every' : 'none' + ); + }); } private isFieldAccess(expr: Expression): boolean { if (isThisExpr(expr)) { return true; } + if (isMemberAccessExpr(expr)) { - return this.isFieldAccess(expr.operand); + if (isFutureExpr(expr.operand) && this.isPostGuard) { + // when writing for post-update, future().field.x is a field access + return true; + } else { + return this.isFieldAccess(expr.operand); + } } - if (isReferenceExpr(expr) && isDataModelField(expr.target.ref)) { + if (isReferenceExpr(expr) && isDataModelField(expr.target.ref) && !this.isPostGuard) { return true; } return false; @@ -153,17 +171,17 @@ export default class ExpressionWriter { const rightIsFieldAccess = this.isFieldAccess(expr.right); if (leftIsFieldAccess && rightIsFieldAccess) { - throw new GeneratorError( - `Comparison between fields are not supported yet` - ); + throw new PluginError(`Comparison between fields are not supported yet`); } if (!leftIsFieldAccess && !rightIsFieldAccess) { // compile down to a plain expression - this.guard(() => { - this.plain(expr.left); - this.writer.write(' ' + operator + ' '); - this.plain(expr.right); + this.block(() => { + this.guard(() => { + this.plain(expr.left); + this.writer.write(' ' + operator + ' '); + this.plain(expr.right); + }); }); return; @@ -180,39 +198,71 @@ export default class ExpressionWriter { operator = this.negateOperator(operator); } - this.writeFieldCondition( - fieldAccess, - () => { - this.block( - () => { - if (this.isModelTyped(fieldAccess)) { - // comparing with an object, conver to "id" comparison instead - this.writer.write('id: '); - this.block(() => { + if (isMemberAccessExpr(fieldAccess) && isFutureExpr(fieldAccess.operand)) { + // future().field should be treated as the "field" directly, so we + // strip 'future().' and synthesize a reference expr + fieldAccess = { + $type: ReferenceExpr, + $container: fieldAccess.$container, + target: fieldAccess.member, + $resolvedType: fieldAccess.$resolvedType, + } as ReferenceExpr; + } + + // if the operand refers to auth(), need to build a guard to avoid + // using undefined user as filter (which means no filter to Prisma) + // if auth() evaluates falsy, just treat the condition as false + if (this.isAuthOrAuthMemberAccess(operand)) { + this.writer.write(`!user ? { ${GUARD_FIELD_NAME}: false } : `); + } + + this.block(() => { + this.writeFieldCondition( + fieldAccess, + () => { + this.block( + () => { + const dataModel = this.isModelTyped(fieldAccess); + if (dataModel) { + const idField = getIdField(dataModel); + if (!idField) { + throw new PluginError(`Data model ${dataModel.name} does not have an id field`); + } + // comparing with an object, convert to "id" comparison instead + this.writer.write(`${idField.name}: `); + this.block(() => { + this.writeOperator(operator, () => { + // operand ? operand.field : null + this.writer.write('('); + this.plain(operand); + this.writer.write(' ? '); + this.plain(operand); + this.writer.write(`.${idField.name}`); + this.writer.write(' : null'); + this.writer.write(')'); + }); + }); + } else { this.writeOperator(operator, () => { this.plain(operand); - this.writer.write('?.id'); }); - }); - } else { - this.writeOperator(operator, () => { - this.plain(operand); - }); - } - }, - // "this" expression is compiled away (to .id access), so we should - // avoid generating a new layer - !isThisExpr(fieldAccess) - ); - }, - 'is' - ); + } + }, + // "this" expression is compiled away (to .id access), so we should + // avoid generating a new layer + !isThisExpr(fieldAccess) + ); + }, + 'is' + ); + }); } - private writeOperator( - operator: ComparisonOperator, - writeOperand: () => void - ) { + private isAuthOrAuthMemberAccess(expr: Expression) { + return isAuthInvocation(expr) || (isMemberAccessExpr(expr) && isAuthInvocation(expr.operand)); + } + + private writeOperator(operator: ComparisonOperator, writeOperand: () => void) { if (operator === '!=') { // wrap a 'not' this.writer.write('not: '); @@ -240,16 +290,19 @@ export default class ExpressionWriter { } else if (isReferenceExpr(fieldAccess)) { selector = fieldAccess.target.ref?.name; } else if (isMemberAccessExpr(fieldAccess)) { - selector = fieldAccess.member.ref?.name; - operand = fieldAccess.operand; + if (isFutureExpr(fieldAccess.operand)) { + // future().field should be treated as the "field" + selector = fieldAccess.member.ref?.name; + } else { + selector = fieldAccess.member.ref?.name; + operand = fieldAccess.operand; + } } else { - throw new GeneratorError( - `Unsupported expression type: ${fieldAccess.$type}` - ); + throw new PluginError(`Unsupported expression type: ${fieldAccess.$type}`); } if (!selector) { - throw new GeneratorError(`Failed to write FieldAccess expression`); + throw new PluginError(`Failed to write FieldAccess expression`); } if (operand) { @@ -294,14 +347,14 @@ export default class ExpressionWriter { private block(write: () => void, condition = true) { if (condition) { - this.writer.block(write); + this.writer.inlineBlock(write); } else { write(); } } private isModelTyped(expr: Expression) { - return isDataModel(expr.$resolvedType?.decl); + return isDataModel(expr.$resolvedType?.decl) ? (expr.$resolvedType?.decl as DataModel) : undefined; } private mapOperator(operator: '==' | '!=' | '>' | '>=' | '<' | '<=') { @@ -313,11 +366,11 @@ export default class ExpressionWriter { case '>': return 'gt'; case '>=': - return 'ge'; + return 'gte'; case '<': return 'lt'; case '<=': - return 'le'; + return 'lte'; } } @@ -337,24 +390,28 @@ export default class ExpressionWriter { } private writeLogical(expr: BinaryExpr, operator: '&&' | '||') { - this.writer.writeLine(`${operator === '&&' ? 'AND' : 'OR'}: `); - this.writeExprList([expr.left, expr.right]); + this.block(() => { + this.writer.write(`${operator === '&&' ? 'AND' : 'OR'}: `); + this.writeExprList([expr.left, expr.right]); + }); } private writeUnary(expr: UnaryExpr) { if (expr.operator !== '!') { - throw new GeneratorError( - `Unary operator "${expr.operator}" is not supported` - ); + throw new PluginError(`Unary operator "${expr.operator}" is not supported`); } - this.writer.writeLine('NOT: '); - this.write(expr.operand); + this.block(() => { + this.writer.write('NOT: '); + this.write(expr.operand); + }); } private writeLiteral(expr: LiteralExpr) { - this.guard(() => { - this.plain(expr); + this.block(() => { + this.guard(() => { + this.plain(expr); + }); }); } } diff --git a/packages/schema/src/plugins/access-policy/index.ts b/packages/schema/src/plugins/access-policy/index.ts new file mode 100644 index 000000000..c47f6e11d --- /dev/null +++ b/packages/schema/src/plugins/access-policy/index.ts @@ -0,0 +1,9 @@ +import { Model } from '@zenstackhq/language/ast'; +import { PluginOptions } from '@zenstackhq/sdk'; +import PolicyGenerator from './policy-guard-generator'; + +export const name = 'Access Policy'; + +export default async function run(model: Model, options: PluginOptions) { + return new PolicyGenerator().generate(model, options); +} diff --git a/packages/schema/src/plugins/access-policy/policy-guard-generator.ts b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts new file mode 100644 index 000000000..c0b9c3b86 --- /dev/null +++ b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts @@ -0,0 +1,384 @@ +import { + DataModel, + Expression, + isBinaryExpr, + isDataModel, + isDataModelField, + isEnum, + isExpression, + isInvocationExpr, + isMemberAccessExpr, + isReferenceExpr, + isUnaryExpr, + MemberAccessExpr, + Model, +} from '@zenstackhq/language/ast'; +import { PolicyKind, PolicyOperationKind } from '@zenstackhq/runtime'; +import { getLiteral, GUARD_FIELD_NAME, PluginError, PluginOptions, resolved } from '@zenstackhq/sdk'; +import { camelCase } from 'change-case'; +import { streamAllContents } from 'langium'; +import path from 'path'; +import { FunctionDeclaration, Project, SourceFile, VariableDeclarationKind } from 'ts-morph'; +import { name } from '.'; +import { isFromStdlib } from '../../language-server/utils'; +import { analyzePolicies, getIdField } from '../../utils/ast-utils'; +import { ALL_OPERATION_KINDS, getDefaultOutputFolder, RUNTIME_PACKAGE } from '../plugin-utils'; +import { ExpressionWriter } from './expression-writer'; +import { isFutureExpr } from './utils'; +import { ZodSchemaGenerator } from './zod-schema-generator'; + +/** + * Generates source file that contains Prisma query guard objects used for injecting database queries + */ +export default class PolicyGenerator { + async generate(model: Model, options: PluginOptions) { + const output = options.output ? (options.output as string) : getDefaultOutputFolder(); + if (!output) { + console.error(`Unable to determine output path, not running plugin ${name}`); + return; + } + + const project = new Project(); + const sf = project.createSourceFile(path.join(output, 'policy.ts'), undefined, { overwrite: true }); + + sf.addImportDeclaration({ + namedImports: [{ name: 'QueryContext' }], + moduleSpecifier: `${RUNTIME_PACKAGE}`, + isTypeOnly: true, + }); + + sf.addImportDeclaration({ + namedImports: [{ name: 'z' }], + moduleSpecifier: 'zod', + }); + + // import enums + for (const e of model.declarations.filter((d) => isEnum(d))) { + sf.addImportDeclaration({ + namedImports: [{ name: e.name }], + moduleSpecifier: '@prisma/client', + }); + } + + const models = model.declarations.filter((d) => isDataModel(d)) as DataModel[]; + + const policyMap: Record> = {}; + for (const model of models) { + policyMap[model.name] = await this.generateQueryGuardForModel(model, sf); + } + + const zodGenerator = new ZodSchemaGenerator(); + + sf.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'policy', + initializer: (writer) => { + writer.block(() => { + writer.write('guard:'); + writer.inlineBlock(() => { + for (const [model, map] of Object.entries(policyMap)) { + writer.write(`${camelCase(model)}:`); + writer.inlineBlock(() => { + for (const [op, func] of Object.entries(map)) { + if (typeof func === 'object') { + writer.write(`${op}: ${JSON.stringify(func)},`); + } else { + writer.write(`${op}: ${func},`); + } + } + }); + writer.write(','); + } + }); + + writer.writeLine(','); + + writer.write('schema:'); + zodGenerator.generate(writer, models); + }); + }, + }, + ], + }); + + sf.addStatements('export default policy'); + + sf.formatText(); + await project.save(); + await project.emit(); + } + + private getPolicyExpressions(model: DataModel, kind: PolicyKind, operation: PolicyOperationKind) { + const attrs = model.attributes.filter((attr) => attr.decl.ref?.name === `@@${kind}`); + + const checkOperation = operation === 'postUpdate' ? 'update' : operation; + + let result = attrs + .filter((attr) => { + const opsValue = getLiteral(attr.args[0].value); + if (!opsValue) { + return false; + } + const ops = opsValue.split(',').map((s) => s.trim()); + return ops.includes(checkOperation) || ops.includes('all'); + }) + .map((attr) => attr.args[1].value); + + if (operation === 'update') { + result = this.processUpdatePolicies(result, false); + } else if (operation === 'postUpdate') { + result = this.processUpdatePolicies(result, true); + } + + return result; + } + + private processUpdatePolicies(expressions: Expression[], postUpdate: boolean) { + return expressions + .map((expr) => this.visitPolicyExpression(expr, postUpdate)) + .filter((e): e is Expression => !!e); + } + + private visitPolicyExpression(expr: Expression, postUpdate: boolean): Expression | undefined { + if (isBinaryExpr(expr) && (expr.operator === '&&' || expr.operator === '||')) { + const left = this.visitPolicyExpression(expr.left, postUpdate); + const right = this.visitPolicyExpression(expr.right, postUpdate); + if (!left) return right; + if (!right) return left; + return { ...expr, left, right }; + } + + if (isUnaryExpr(expr) && expr.operator === '!') { + const operand = this.visitPolicyExpression(expr.operand, postUpdate); + if (!operand) return undefined; + return { ...expr, operand }; + } + + if (postUpdate && !this.hasFutureReference(expr)) { + return undefined; + } else if (!postUpdate && this.hasFutureReference(expr)) { + return undefined; + } + + return expr; + } + + private hasFutureReference(expr: Expression) { + for (const node of streamAllContents(expr)) { + if (isInvocationExpr(node) && node.function.ref?.name === 'future' && isFromStdlib(node.function.ref)) { + return true; + } + } + return false; + } + + private async generateQueryGuardForModel(model: DataModel, sourceFile: SourceFile) { + const result: Record = {}; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const policies: any = analyzePolicies(model); + + for (const kind of ALL_OPERATION_KINDS) { + if (policies[kind] === true || policies[kind] === false) { + result[kind] = policies[kind]; + continue; + } + + const denies = this.getPolicyExpressions(model, 'deny', kind); + const allows = this.getPolicyExpressions(model, 'allow', kind); + + if (kind === 'update' && allows.length === 0) { + // no allow rule for 'update', policy is constant based on if there's + // post-update counterpart + if (this.getPolicyExpressions(model, 'allow', 'postUpdate').length === 0) { + result[kind] = false; + continue; + } else { + result[kind] = true; + continue; + } + } + + if (kind === 'postUpdate' && allows.length === 0 && denies.length === 0) { + // no rule 'postUpdate', always allow + result[kind] = true; + continue; + } + + const func = this.generateQueryGuardFunction(sourceFile, model, kind, allows, denies); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + result[kind] = func.getName()!; + + if (kind === 'postUpdate') { + const preValueSelect = this.generatePreValueSelect(model, allows, denies); + if (preValueSelect) { + result['preValueSelect'] = preValueSelect; + } + } + } + return result; + } + + // generates an object that can be used as the 'select' argument when fetching pre-update + // entity value + private generatePreValueSelect(model: DataModel, allows: Expression[], denies: Expression[]): object { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = {}; + const addPath = (path: string[]) => { + let curr = result; + path.forEach((seg, i) => { + if (i === path.length - 1) { + curr[seg] = true; + } else { + if (!curr[seg]) { + curr[seg] = { select: {} }; + } + curr = curr[seg].select; + } + }); + }; + + const visit = (node: Expression): string[] | undefined => { + if (isReferenceExpr(node)) { + const target = resolved(node.target); + if (isDataModelField(target)) { + // a field selection, it's a terminal + return [target.name]; + } + } else if (isMemberAccessExpr(node)) { + if (isFutureExpr(node.operand)) { + // future().field is not subject to pre-update select + return undefined; + } + + // build a selection path inside-out for chained member access + const inner = visit(node.operand); + if (inner) { + return [...inner, node.member.$refText]; + } + } + return undefined; + }; + + for (const rule of [...allows, ...denies]) { + for (const expr of streamAllContents(rule).filter((node): node is Expression => isExpression(node))) { + // only care about member access and reference expressions + if (!isMemberAccessExpr(expr) && !isReferenceExpr(expr)) { + continue; + } + + if (expr.$container.$type === MemberAccessExpr) { + // only visit top-level member access + continue; + } + + const path = visit(expr); + if (path) { + if (isDataModel(expr.$resolvedType?.decl)) { + // member selection ended at a data model field, include its 'id' + path.push('id'); + } + addPath(path); + } + } + } + + return Object.keys(result).length === 0 ? null : result; + } + + private generateQueryGuardFunction( + sourceFile: SourceFile, + model: DataModel, + kind: PolicyOperationKind, + allows: Expression[], + denies: Expression[] + ): FunctionDeclaration { + const func = sourceFile + .addFunction({ + name: model.name + '_' + kind, + returnType: 'any', + parameters: [ + { + name: 'context', + type: 'QueryContext', + }, + ], + }) + .addBody(); + + // check if any allow or deny rule contains 'auth()' invocation + let hasAuthRef = false; + for (const node of [...denies, ...allows]) { + for (const child of streamAllContents(node)) { + if (isInvocationExpr(child) && resolved(child.function).name === 'auth') { + hasAuthRef = true; + break; + } + } + if (hasAuthRef) { + break; + } + } + + if (hasAuthRef) { + const userModel = model.$container.declarations.find( + (decl): decl is DataModel => isDataModel(decl) && decl.name === 'User' + ); + if (!userModel) { + throw new PluginError('User model not found'); + } + const userIdField = getIdField(userModel); + if (!userIdField) { + throw new PluginError('User model does not have an id field'); + } + + // normalize user to null to avoid accidentally use undefined in filter + func.addStatements(`const user = context.user ?? null;`); + } + + // r = ; + func.addStatements((writer) => { + writer.write('return '); + const exprWriter = new ExpressionWriter(writer, kind === 'postUpdate'); + const writeDenies = () => { + writer.conditionalWrite(denies.length > 1, '{ AND: ['); + denies.forEach((expr, i) => { + writer.inlineBlock(() => { + writer.write('NOT: '); + exprWriter.write(expr); + }); + writer.conditionalWrite(i !== denies.length - 1, ','); + }); + writer.conditionalWrite(denies.length > 1, ']}'); + }; + + const writeAllows = () => { + writer.conditionalWrite(allows.length > 1, '{ OR: ['); + allows.forEach((expr, i) => { + exprWriter.write(expr); + writer.conditionalWrite(i !== allows.length - 1, ','); + }); + writer.conditionalWrite(allows.length > 1, ']}'); + }; + + if (allows.length > 0 && denies.length > 0) { + writer.write('{ AND: ['); + writeDenies(); + writer.write(','); + writeAllows(); + writer.write(']}'); + } else if (denies.length > 0) { + writeDenies(); + } else if (allows.length > 0) { + writeAllows(); + } else { + // disallow any operation + writer.write(`{ ${GUARD_FIELD_NAME}: false }`); + } + writer.write(';'); + }); + return func; + } +} diff --git a/packages/schema/src/generator/prisma/typescript-expression-transformer.ts b/packages/schema/src/plugins/access-policy/typescript-expression-transformer.ts similarity index 56% rename from packages/schema/src/generator/prisma/typescript-expression-transformer.ts rename to packages/schema/src/plugins/access-policy/typescript-expression-transformer.ts index e4e2deaf0..961b3028f 100644 --- a/packages/schema/src/generator/prisma/typescript-expression-transformer.ts +++ b/packages/schema/src/plugins/access-policy/typescript-expression-transformer.ts @@ -1,4 +1,3 @@ -import { GeneratorError } from '../types'; import { ArrayExpr, Expression, @@ -10,12 +9,22 @@ import { NullExpr, ReferenceExpr, ThisExpr, -} from '@lang/generated/ast'; +} from '@zenstackhq/language/ast'; +import { PluginError } from '@zenstackhq/sdk'; +import { isAuthInvocation } from '../../utils/ast-utils'; +import { isFutureExpr } from './utils'; /** * Transforms ZModel expression to plain TypeScript expression. */ export default class TypeScriptExpressionTransformer { + /** + * Constructs a new TypeScriptExpressionTransformer. + * + * @param isPostGuard indicates if we're writing for post-update conditions + */ + constructor(private readonly isPostGuard = false) {} + /** * * @param expr @@ -45,9 +54,7 @@ export default class TypeScriptExpressionTransformer { return this.memberAccess(expr as MemberAccessExpr); default: - throw new GeneratorError( - `Unsupported expression type: ${expr.$type}` - ); + throw new PluginError(`Unsupported expression type: ${expr.$type}`); } } @@ -59,34 +66,46 @@ export default class TypeScriptExpressionTransformer { private memberAccess(expr: MemberAccessExpr) { if (!expr.member.ref) { - throw new GeneratorError(`Unresolved MemberAccessExpr`); + throw new PluginError(`Unresolved MemberAccessExpr`); } if (isThisExpr(expr.operand)) { return expr.member.ref.name; + } else if (isFutureExpr(expr.operand)) { + if (!this.isPostGuard) { + throw new PluginError(`future() is only supported in postUpdate rules`); + } + return expr.member.ref.name; } else { - return `${this.transform(expr.operand)}?.${expr.member.ref.name}`; + // normalize field access to null instead of undefined to avoid accidentally use undefined in filter + return `(${this.transform(expr.operand)} ? ${this.transform(expr.operand)}.${expr.member.ref.name} : null)`; } } private invocation(expr: InvocationExpr) { - if (expr.function.ref?.name !== 'auth') { - throw new GeneratorError( - `Function invocation is not supported: ${expr.function.ref?.name}` - ); + if (isAuthInvocation(expr)) { + return 'user'; + } else { + throw new PluginError(`Function invocation is not supported: ${expr.function.ref?.name}`); } - return 'user'; } private reference(expr: ReferenceExpr) { if (!expr.target.ref) { - throw new GeneratorError(`Unresolved ReferenceExpr`); + throw new PluginError(`Unresolved ReferenceExpr`); } if (isEnumField(expr.target.ref)) { return `${expr.target.ref.$container.name}.${expr.target.ref.name}`; } else { - return expr.target.ref.name; + if (this.isPostGuard) { + // if we're processing post-update, any direct field access should be + // treated as access to context.preValue, which is entity's value before + // the update + return `context.preValue?.${expr.target.ref.name}`; + } else { + return expr.target.ref.name; + } } } diff --git a/packages/schema/src/plugins/access-policy/utils.ts b/packages/schema/src/plugins/access-policy/utils.ts new file mode 100644 index 000000000..741d686d9 --- /dev/null +++ b/packages/schema/src/plugins/access-policy/utils.ts @@ -0,0 +1,9 @@ +import { Expression, isInvocationExpr } from '@zenstackhq/language/ast'; +import { isFromStdlib } from '../../language-server/utils'; + +/** + * Returns if the given expression is a "future()" method call. + */ +export function isFutureExpr(expr: Expression) { + return !!(isInvocationExpr(expr) && expr.function.ref?.name === 'future' && isFromStdlib(expr.function.ref)); +} diff --git a/packages/schema/src/plugins/access-policy/zod-schema-generator.ts b/packages/schema/src/plugins/access-policy/zod-schema-generator.ts new file mode 100644 index 000000000..222e17186 --- /dev/null +++ b/packages/schema/src/plugins/access-policy/zod-schema-generator.ts @@ -0,0 +1,164 @@ +import { DataModel, DataModelField, DataModelFieldAttribute, isDataModelField } from '@zenstackhq/language/ast'; +import { AUXILIARY_FIELDS, getLiteral } from '@zenstackhq/sdk'; +import { camelCase } from 'change-case'; +import { CodeBlockWriter } from 'ts-morph'; +import { VALIDATION_ATTRIBUTES } from '../../utils/ast-utils'; + +/** + * Writes Zod schema for data models. + */ +export class ZodSchemaGenerator { + generate(writer: CodeBlockWriter, models: DataModel[]) { + writer.inlineBlock(() => { + models.forEach((model) => { + const fields = model.fields.filter( + (field) => + !AUXILIARY_FIELDS.includes(field.name) && + // scalar fields only + !isDataModelField(field.type.reference?.ref) && + this.hasValidationAttributes(field) + ); + + if (fields.length === 0) { + return; + } + + writer.write(`${camelCase(model.name)}: z.object(`); + writer.inlineBlock(() => { + fields.forEach((field) => { + writer.writeLine(`${field.name}: ${this.makeFieldValidator(field)},`); + }); + }); + writer.writeLine(').partial(),'); + }); + }); + } + + private hasValidationAttributes(field: DataModelField) { + return field.attributes.some((attr) => VALIDATION_ATTRIBUTES.includes(attr.decl.$refText)); + } + + private makeFieldValidator(field: DataModelField) { + let schema = this.makeZodSchema(field); + // translate field constraint attributes to zod schema + for (const attr of field.attributes) { + switch (attr.decl.ref?.name) { + case '@length': { + const min = this.getAttrLiteralArg(attr, 'min'); + if (min) { + schema += `.min(${min})`; + } + const max = this.getAttrLiteralArg(attr, 'max'); + if (max) { + schema += `.max(${max})`; + } + break; + } + case '@regex': { + const expr = this.getAttrLiteralArg(attr, 'regex'); + if (expr) { + schema += `.regex(/${expr}/)`; + } + break; + } + case '@startsWith': { + const text = this.getAttrLiteralArg(attr, 'text'); + if (text) { + schema += `.startsWith(${JSON.stringify(text)})`; + } + break; + } + case '@endsWith': { + const text = this.getAttrLiteralArg(attr, 'text'); + if (text) { + schema += `.endsWith(${JSON.stringify(text)})`; + } + break; + } + case '@email': { + schema += `.email()`; + break; + } + case '@url': { + schema += `.url()`; + break; + } + case '@datetime': { + schema += `.datetime({ offset: true })`; + break; + } + case '@gt': { + const value = this.getAttrLiteralArg(attr, 'value'); + if (value !== undefined) { + schema += `.gt(${value})`; + } + break; + } + case '@gte': { + const value = this.getAttrLiteralArg(attr, 'value'); + if (value !== undefined) { + schema += `.gte(${value})`; + } + break; + } + case '@lt': { + const value = this.getAttrLiteralArg(attr, 'value'); + if (value !== undefined) { + schema += `.lt(${value})`; + } + break; + } + case '@lte': { + const value = this.getAttrLiteralArg(attr, 'value'); + if (value !== undefined) { + schema += `.lte(${value})`; + } + break; + } + } + } + + if (field.type.optional) { + schema += '.nullable()'; + } + + return schema; + } + + private makeZodSchema(field: DataModelField) { + let schema: string; + switch (field.type.type) { + case 'Int': + case 'Float': + case 'Decimal': + schema = 'z.number()'; + break; + case 'BigInt': + schema = 'z.bigint()'; + break; + case 'String': + schema = 'z.string()'; + break; + case 'Boolean': + schema = 'z.boolean()'; + break; + case 'DateTime': + schema = 'z.date()'; + break; + default: + schema = 'z.any()'; + break; + } + + if (field.type.array) { + schema = `z.array(${schema})`; + } + + return schema; + } + + private getAttrLiteralArg(attr: DataModelFieldAttribute, paramName: string) { + const arg = attr.args.find((arg) => arg.$resolvedParam?.name === paramName); + return arg && getLiteral(arg.value); + } +} diff --git a/packages/schema/src/plugins/model-meta/index.ts b/packages/schema/src/plugins/model-meta/index.ts new file mode 100644 index 000000000..ba708c951 --- /dev/null +++ b/packages/schema/src/plugins/model-meta/index.ts @@ -0,0 +1,121 @@ +import { DataModel, DataModelField, Model, isDataModel, isLiteralExpr } from '@zenstackhq/language/ast'; +import { RuntimeAttribute } from '@zenstackhq/runtime'; +import { PluginOptions, getLiteral, resolved } from '@zenstackhq/sdk'; +import { camelCase } from 'change-case'; +import path from 'path'; +import { CodeBlockWriter, Project, VariableDeclarationKind } from 'ts-morph'; +import { ensureNodeModuleFolder, getDefaultOutputFolder } from '../plugin-utils'; + +export const name = 'Model Metadata'; + +export default async function run(model: Model, options: PluginOptions) { + const output = options.output ? (options.output as string) : getDefaultOutputFolder(); + if (!output) { + console.error(`Unable to determine output path, not running plugin ${name}`); + return; + } + + const dataModels = model.declarations.filter((d): d is DataModel => isDataModel(d)); + + const project = new Project(); + + if (!options.output) { + ensureNodeModuleFolder(output); + } + + const sf = project.createSourceFile(path.join(output, 'model-meta.ts'), undefined, { overwrite: true }); + sf.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [{ name: 'metadata', initializer: (writer) => generateModelMetadata(dataModels, writer) }], + }); + sf.addStatements('export default metadata;'); + + sf.formatText(); + + await project.save(); + await project.emit(); +} + +function generateModelMetadata(dataModels: DataModel[], writer: CodeBlockWriter) { + writer.block(() => { + writer.write('fields:'); + writer.block(() => { + for (const model of dataModels) { + writer.write(`${camelCase(model.name)}:`); + writer.block(() => { + for (const f of model.fields) { + const backlink = getBackLink(f); + writer.write(`${f.name}: { + name: "${f.name}", + type: "${ + f.type.reference + ? f.type.reference.$refText + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + f.type.type! + }", + isId: ${isIdField(f)}, + isDataModel: ${isDataModel(f.type.reference?.ref)}, + isArray: ${f.type.array}, + isOptional: ${f.type.optional}, + attributes: ${JSON.stringify(getFieldAttributes(f))}, + backLink: ${backlink ? "'" + backlink + "'" : 'undefined'} + },`); + } + }); + writer.write(','); + } + }); + writer.write(','); + }); +} + +function getBackLink(field: DataModelField) { + if (!field.type.reference?.ref || !isDataModel(field.type.reference?.ref)) { + return undefined; + } + + const relName = getRelationName(field); + + const sourceModel = field.$container as DataModel; + const targetModel = field.type.reference.ref as DataModel; + + for (const otherField of targetModel.fields) { + if (otherField.type.reference?.ref === sourceModel) { + if (relName) { + const otherRelName = getRelationName(otherField); + if (relName === otherRelName) { + return otherField.name; + } + } else { + return otherField.name; + } + } + } + return undefined; +} + +function getRelationName(field: DataModelField) { + const relAttr = field.attributes.find((attr) => attr.decl.ref?.name === 'relation'); + const relName = relAttr && relAttr.args?.[0] && getLiteral(relAttr.args?.[0].value); + return relName; +} + +function getFieldAttributes(field: DataModelField): RuntimeAttribute[] { + return field.attributes + .map((attr) => { + const args: Array<{ name?: string; value: unknown }> = []; + for (const arg of attr.args) { + if (!isLiteralExpr(arg.value)) { + // attributes with non-literal args are skipped + return undefined; + } + args.push({ name: arg.name, value: arg.value.value }); + } + return { name: resolved(attr.decl).name, args }; + }) + .filter((d): d is RuntimeAttribute => !!d); +} + +function isIdField(field: DataModelField) { + return field.attributes.some((attr) => attr.decl.ref?.name === '@id'); +} diff --git a/packages/schema/src/plugins/plugin-utils.ts b/packages/schema/src/plugins/plugin-utils.ts new file mode 100644 index 000000000..95cfe1ab2 --- /dev/null +++ b/packages/schema/src/plugins/plugin-utils.ts @@ -0,0 +1,48 @@ +import { PolicyOperationKind } from '@zenstackhq/runtime'; +import fs from 'fs'; +import path from 'path'; + +export const RUNTIME_PACKAGE = '@zenstackhq/runtime'; +export const ALL_OPERATION_KINDS: PolicyOperationKind[] = ['create', 'update', 'postUpdate', 'read', 'delete']; + +/** + * Gets the nearest "node_modules" folder by walking up froma start path. + */ +export function getNodeModulesFolder(startPath?: string): string | undefined { + startPath = startPath ?? process.cwd(); + if (fs.existsSync(path.join(startPath, 'node_modules'))) { + return path.join(startPath, 'node_modules'); + } else if (startPath !== '/') { + const parent = path.join(startPath, '..'); + return getNodeModulesFolder(parent); + } else { + return undefined; + } +} + +/** + * Gets the default node_modules/.zenstack output folder for plugins. + * @returns + */ +export function getDefaultOutputFolder() { + const modulesFolder = getNodeModulesFolder(); + return modulesFolder ? path.join(modulesFolder, '.zenstack') : undefined; +} + +/** + * Ensure a folder exists and has a package.json in it. + */ +export function ensureNodeModuleFolder(folder: string) { + if (!fs.existsSync(folder)) { + fs.mkdirSync(folder, { recursive: true }); + } + if (!fs.existsSync(path.join(folder, 'package.json'))) { + fs.writeFileSync( + path.join(folder, 'package.json'), + JSON.stringify({ + name: '.zenstack', + version: '1.0.0', + }) + ); + } +} diff --git a/packages/schema/src/utils/indent-string.ts b/packages/schema/src/plugins/prisma/indent-string.ts similarity index 100% rename from packages/schema/src/utils/indent-string.ts rename to packages/schema/src/plugins/prisma/indent-string.ts diff --git a/packages/schema/src/plugins/prisma/index.ts b/packages/schema/src/plugins/prisma/index.ts new file mode 100644 index 000000000..c9bb7ac08 --- /dev/null +++ b/packages/schema/src/plugins/prisma/index.ts @@ -0,0 +1,9 @@ +import { Model } from '@zenstackhq/language/ast'; +import { PluginOptions } from '@zenstackhq/sdk'; +import PrismaSchemaGenerator from './schema-generator'; + +export const name = 'Prisma'; + +export default async function run(model: Model, options: PluginOptions) { + return new PrismaSchemaGenerator().generate(model, options); +} diff --git a/packages/schema/src/generator/prisma/prisma-builder.ts b/packages/schema/src/plugins/prisma/prisma-builder.ts similarity index 55% rename from packages/schema/src/generator/prisma/prisma-builder.ts rename to packages/schema/src/plugins/prisma/prisma-builder.ts index cec02bb06..6c3da1096 100644 --- a/packages/schema/src/generator/prisma/prisma-builder.ts +++ b/packages/schema/src/plugins/prisma/prisma-builder.ts @@ -1,4 +1,4 @@ -import indentString from '../../utils/indent-string'; +import indentString from './indent-string'; /** * Prisma schema builder @@ -9,29 +9,14 @@ export class PrismaModel { private models: Model[] = []; private enums: Enum[] = []; - addDataSource( - name: string, - provider: string, - url: DataSourceUrl, - shadowDatabaseUrl?: DataSourceUrl - ): DataSource { + addDataSource(name: string, provider: string, url: DataSourceUrl, shadowDatabaseUrl?: DataSourceUrl): DataSource { const ds = new DataSource(name, provider, url, shadowDatabaseUrl); this.datasources.push(ds); return ds; } - addGenerator( - name: string, - provider: string, - output: string, - previewFeatures?: string[] - ): Generator { - const generator = new Generator( - name, - provider, - output, - previewFeatures - ); + addGenerator(name: string, fields: Array<{ name: string; value: string | string[] }>): Generator { + const generator = new Generator(name, fields); this.generators.push(generator); return generator; } @@ -49,12 +34,7 @@ export class PrismaModel { } toString(): string { - return [ - ...this.datasources, - ...this.generators, - ...this.enums, - ...this.models, - ] + return [...this.datasources, ...this.generators, ...this.enums, ...this.models] .map((d) => d.toString()) .join('\n\n'); } @@ -73,9 +53,7 @@ export class DataSource { `datasource ${this.name} {\n` + indentString(`provider="${this.provider}"\n`) + indentString(`url=${this.url}\n`) + - (this.shadowDatabaseUrl - ? indentString(`shadowDatabaseurl=${this.shadowDatabaseUrl}\n`) - : '') + + (this.shadowDatabaseUrl ? indentString(`shadowDatabaseurl=${this.shadowDatabaseUrl}\n`) : '') + `}` ); } @@ -90,41 +68,43 @@ export class DataSourceUrl { } export class Generator { - constructor( - public name: string, - public provider: string, - public output: string, - public previewFeatures?: string[] - ) {} + constructor(public name: string, public fields: Array<{ name: string; value: string | string[] }>) {} toString(): string { return ( `generator ${this.name} {\n` + - indentString(`provider = "${this.provider}"\n`) + - indentString(`output = ${JSON.stringify(this.output)}\n`) + - (this.previewFeatures - ? indentString( - `previewFeatures = [${this.previewFeatures - ?.map((f) => '"' + f + '"') - .join(', ')}]\n` - ) - : '') + - `}` + this.fields.map((f) => indentString(`${f.name} = ${JSON.stringify(f.value)}`)).join('\n') + + `\n}` ); } } -export class Model { +export class DeclarationBase { + public documentations: string[] = []; + + addComment(name: string): string { + this.documentations.push(name); + return name; + } + + toString(): string { + return this.documentations.map((x) => `${x}\n`).join(''); + } +} +export class Model extends DeclarationBase { public fields: ModelField[] = []; public attributes: ModelAttribute[] = []; - constructor(public name: string) {} + constructor(public name: string, public documentations: string[] = []) { + super(); + } addField( name: string, type: ModelFieldType | string, - attributes: FieldAttribute[] = [] + attributes: FieldAttribute[] = [], + documentations: string[] = [] ): ModelField { - const field = new ModelField(name, type, attributes); + const field = new ModelField(name, type, attributes, documentations); this.fields.push(field); return field; } @@ -137,12 +117,9 @@ export class Model { toString(): string { return ( + super.toString() + `model ${this.name} {\n` + - indentString( - [...this.fields, ...this.attributes] - .map((d) => d.toString()) - .join('\n') - ) + + indentString([...this.fields, ...this.attributes].map((d) => d.toString()).join('\n')) + `\n}` ); } @@ -161,25 +138,22 @@ export type ScalarTypes = | 'Unsupported'; export class ModelFieldType { - constructor( - public type: ScalarTypes | string, - public array?: boolean, - public optional?: boolean - ) {} + constructor(public type: ScalarTypes | string, public array?: boolean, public optional?: boolean) {} toString(): string { - return `${this.type}${this.array ? '[]' : ''}${ - this.optional ? '?' : '' - }`; + return `${this.type}${this.array ? '[]' : ''}${this.optional ? '?' : ''}`; } } -export class ModelField { +export class ModelField extends DeclarationBase { constructor( public name: string, public type: ModelFieldType | string, - public attributes: FieldAttribute[] = [] - ) {} + public attributes: FieldAttribute[] = [], + public documentations: string[] = [] + ) { + super(); + } addAttribute(name: string, args: AttributeArg[] = []): FieldAttribute { const attr = new FieldAttribute(name, args); @@ -189,10 +163,9 @@ export class ModelField { toString(): string { return ( + super.toString() + `${this.name} ${this.type}` + - (this.attributes.length > 0 - ? ' ' + this.attributes.map((a) => a.toString()).join(' ') - : '') + (this.attributes.length > 0 ? ' ' + this.attributes.map((a) => a.toString()).join(' ') : '') ); } } @@ -201,11 +174,7 @@ export class FieldAttribute { constructor(public name: string, public args: AttributeArg[] = []) {} toString(): string { - return ( - `${this.name}(` + - this.args.map((a) => a.toString()).join(', ') + - `)` - ); + return `${this.name}(` + this.args.map((a) => a.toString()).join(', ') + `)`; } } @@ -213,71 +182,42 @@ export class ModelAttribute { constructor(public name: string, public args: AttributeArg[] = []) {} toString(): string { - return ( - `${this.name}(` + - this.args.map((a) => a.toString()).join(', ') + - `)` - ); + return `${this.name}(` + this.args.map((a) => a.toString()).join(', ') + `)`; } } export class AttributeArg { - constructor( - public name: string | undefined, - public value: AttributeArgValue - ) {} + constructor(public name: string | undefined, public value: AttributeArgValue) {} toString(): string { - return this.name - ? `${this.name}: ${this.value}` - : this.value.toString(); + return this.name ? `${this.name}: ${this.value}` : this.value.toString(); } } export class AttributeArgValue { constructor( - public type: - | 'String' - | 'FieldReference' - | 'Number' - | 'Boolean' - | 'Array' - | 'FunctionCall', - public value: - | string - | number - | boolean - | FieldReference - | FunctionCall - | AttributeArgValue[] + public type: 'String' | 'FieldReference' | 'Number' | 'Boolean' | 'Array' | 'FunctionCall', + public value: string | number | boolean | FieldReference | FunctionCall | AttributeArgValue[] ) { switch (type) { case 'String': - if (typeof value !== 'string') - throw new Error('Value must be string'); + if (typeof value !== 'string') throw new Error('Value must be string'); break; case 'Number': - if (typeof value !== 'number') - throw new Error('Value must be number'); + if (typeof value !== 'number') throw new Error('Value must be number'); break; case 'Boolean': - if (typeof value !== 'boolean') - throw new Error('Value must be boolean'); + if (typeof value !== 'boolean') throw new Error('Value must be boolean'); break; case 'Array': - if (!Array.isArray(value)) - throw new Error('Value must be array'); + if (!Array.isArray(value)) throw new Error('Value must be array'); break; case 'FieldReference': - if ( - typeof value !== 'string' && - !(value instanceof FieldReference) - ) + if (typeof value !== 'string' && !(value instanceof FieldReference)) throw new Error('Value must be string or FieldReference'); break; case 'FunctionCall': - if (!(value instanceof FunctionCall)) - throw new Error('Value must be FunctionCall'); + if (!(value instanceof FunctionCall)) throw new Error('Value must be FunctionCall'); break; } } @@ -295,10 +235,7 @@ export class AttributeArgValue { const fr = this.value as FieldReference; let r = fr.field; if (fr.args.length > 0) { - r += - '(' + - fr.args.map((a) => a.toString()).join(',') + - ')'; + r += '(' + fr.args.map((a) => a.toString()).join(',') + ')'; } return r; } @@ -308,13 +245,7 @@ export class AttributeArgValue { case 'Boolean': return this.value ? 'true' : 'false'; case 'Array': - return ( - '[' + - (this.value as AttributeArgValue[]) - .map((v) => v.toString()) - .join(', ') + - ']' - ); + return '[' + (this.value as AttributeArgValue[]).map((v) => v.toString()).join(', ') + ']'; default: throw new Error(`Unknown attribute value type ${this.type}`); } @@ -337,12 +268,7 @@ export class FunctionCall { constructor(public func: string, public args: FunctionCallArg[] = []) {} toString(): string { - return ( - `${this.func}` + - '(' + - this.args.map((a) => a.toString()).join(', ') + - ')' - ); + return `${this.func}` + '(' + this.args.map((a) => a.toString()).join(', ') + ')'; } } @@ -359,11 +285,7 @@ export class Enum { constructor(public name: string, public fields: EnumField[]) {} toString(): string { - return ( - `enum ${this.name} {\n` + - indentString(this.fields.join('\n')) + - '\n}' - ); + return `enum ${this.name} {\n` + indentString(this.fields.join('\n')) + '\n}'; } } diff --git a/packages/schema/src/generator/prisma/schema-generator.ts b/packages/schema/src/plugins/prisma/schema-generator.ts similarity index 52% rename from packages/schema/src/generator/prisma/schema-generator.ts rename to packages/schema/src/plugins/prisma/schema-generator.ts index 1584ff35d..6bd8ba94b 100644 --- a/packages/schema/src/generator/prisma/schema-generator.ts +++ b/packages/schema/src/plugins/prisma/schema-generator.ts @@ -1,4 +1,5 @@ import { + AstNode, Attribute, AttributeArg, DataModel, @@ -8,45 +9,62 @@ import { DataSource, Enum, Expression, + GeneratorDecl, InvocationExpr, + LiteralExpr, + Model, isArrayExpr, isInvocationExpr, isLiteralExpr, isReferenceExpr, - LiteralExpr, -} from '@lang/generated/ast'; +} from '@zenstackhq/language/ast'; +import { + GUARD_FIELD_NAME, + PluginError, + PluginOptions, + TRANSACTION_FIELD_NAME, + getLiteral, + getLiteralArray, + resolved, +} from '@zenstackhq/sdk'; +import fs from 'fs'; import { writeFile } from 'fs/promises'; -import { AstNode } from 'langium'; import path from 'path'; -import { GUARD_FIELD_NAME, TRANSACTION_FIELD_NAME } from '../constants'; -import { Context, GeneratorError } from '../types'; -import { resolved } from '../ast-utils'; +import { analyzePolicies } from '../../utils/ast-utils'; +import { execSync } from '../../utils/exec-utils'; +import ZModelCodeGenerator from './zmodel-code-generator'; import { + ModelFieldType, AttributeArg as PrismaAttributeArg, AttributeArgValue as PrismaAttributeArgValue, + Model as PrismaDataModel, DataSourceUrl as PrismaDataSourceUrl, FieldAttribute as PrismaFieldAttribute, - ModelAttribute as PrismaModelAttribute, - Model as PrismaDataModel, FieldReference as PrismaFieldReference, FieldReferenceArg as PrismaFieldReferenceArg, FunctionCall as PrismaFunctionCall, FunctionCallArg as PrismaFunctionCallArg, PrismaModel, - ModelFieldType, + ModelAttribute as PrismaModelAttribute, } from './prisma-builder'; /** * Generates Prisma schema file */ export default class PrismaSchemaGenerator { - constructor(private readonly context: Context) {} + private zModelGenerator: ZModelCodeGenerator = new ZModelCodeGenerator(); + + private readonly PRELUDE = `////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// - async generate(): Promise { - const { schema } = this.context; +`; + + async generate(model: Model, options: PluginOptions) { const prisma = new PrismaModel(); - for (const decl of schema.declarations) { + for (const decl of model.declarations) { switch (decl.$type) { case DataSource: this.generateDataSource(prisma, decl as DataSource); @@ -59,14 +77,21 @@ export default class PrismaSchemaGenerator { case DataModel: this.generateModel(prisma, decl as DataModel); break; + + case GeneratorDecl: + this.generateGenerator(prisma, decl as GeneratorDecl); + break; } } - this.generateGenerator(prisma); + const outFile = (options.output as string) ?? './prisma/schema.prisma'; + if (!fs.existsSync(path.dirname(outFile))) { + fs.mkdirSync(path.dirname(outFile), { recursive: true }); + } + await writeFile(outFile, this.PRELUDE + prisma.toString()); - const outFile = path.join(this.context.outDir, 'schema.prisma'); - await writeFile(outFile, prisma.toString()); - return outFile; + // run 'prisma generate' + await execSync(`npx prisma generate --schema ${outFile}`); } private generateDataSource(prisma: PrismaModel, dataSource: DataSource) { @@ -80,9 +105,7 @@ export default class PrismaSchemaGenerator { if (this.isStringLiteral(f.value)) { provider = f.value.value as string; } else { - throw new GeneratorError( - 'Datasource provider must be set to a string' - ); + throw new PluginError('Datasource provider must be set to a string'); } break; } @@ -90,9 +113,7 @@ export default class PrismaSchemaGenerator { case 'url': { const r = this.extractDataSourceUrl(f.value); if (!r) { - throw new GeneratorError( - 'Invalid value for datasource url' - ); + throw new PluginError('Invalid value for datasource url'); } url = r; break; @@ -101,9 +122,7 @@ export default class PrismaSchemaGenerator { case 'shadowDatabaseUrl': { const r = this.extractDataSourceUrl(f.value); if (!r) { - throw new GeneratorError( - 'Invalid value for datasource url' - ); + throw new PluginError('Invalid value for datasource url'); } shadowDatabaseUrl = r; break; @@ -112,10 +131,10 @@ export default class PrismaSchemaGenerator { } if (!provider) { - throw new GeneratorError('Datasource is missing "provider" field'); + throw new PluginError('Datasource is missing "provider" field'); } if (!url) { - throw new GeneratorError('Datasource is missing "url" field'); + throw new PluginError('Datasource is missing "url" field'); } prisma.addDataSource(dataSource.name, provider, url, shadowDatabaseUrl); @@ -130,21 +149,19 @@ export default class PrismaSchemaGenerator { fieldValue.args.length === 1 && this.isStringLiteral(fieldValue.args[0].value) ) { - return new PrismaDataSourceUrl( - fieldValue.args[0].value.value as string, - true - ); + return new PrismaDataSourceUrl(fieldValue.args[0].value.value as string, true); } else { return null; } } - private generateGenerator(prisma: PrismaModel) { + private generateGenerator(prisma: PrismaModel, decl: GeneratorDecl) { prisma.addGenerator( - 'client', - 'prisma-client-js', - path.join('..', this.context.generatedCodeDir, '.prisma'), - ['fieldReference'] + decl.name, + decl.fields.map((f) => { + const value = isArrayExpr(f.value) ? getLiteralArray(f.value) : getLiteral(f.value); + return { name: f.name, value }; + }) ); } @@ -154,37 +171,42 @@ export default class PrismaSchemaGenerator { this.generateModelField(model, field); } - // add an "zenstack_guard" field for dealing with pure auth() related conditions - model.addField(GUARD_FIELD_NAME, 'Boolean', [ - new PrismaFieldAttribute('@default', [ + const { allowAll, denyAll, hasFieldValidation } = analyzePolicies(decl); + + if ((!allowAll && !denyAll) || hasFieldValidation) { + // generate auxiliary fields for policy check + + // add an "zenstack_guard" field for dealing with pure auth() related conditions + model.addField(GUARD_FIELD_NAME, 'Boolean', [ + new PrismaFieldAttribute('@default', [ + new PrismaAttributeArg(undefined, new PrismaAttributeArgValue('Boolean', true)), + ]), + ]); + + // add an "zenstack_transaction" field for tracking records created/updated with nested writes + model.addField(TRANSACTION_FIELD_NAME, 'String?'); + + // create an index for "zenstack_transaction" field + model.addAttribute('@@index', [ new PrismaAttributeArg( undefined, - new PrismaAttributeArgValue('Boolean', true) + new PrismaAttributeArgValue('Array', [ + new PrismaAttributeArgValue('FieldReference', TRANSACTION_FIELD_NAME), + ]) ), - ]), - ]); - - // add an "zenstack_transaction" field for tracking records created/updated with nested writes - model.addField(TRANSACTION_FIELD_NAME, 'String?'); - - // create an index for "zenstack_transaction" field - model.addAttribute('@@index', [ - new PrismaAttributeArg( - undefined, - new PrismaAttributeArgValue('Array', [ - new PrismaAttributeArgValue( - 'FieldReference', - TRANSACTION_FIELD_NAME - ), - ]) - ), - ]); - - for (const attr of decl.attributes.filter( - (attr) => attr.decl.ref && this.isPrismaAttribute(attr.decl.ref) - )) { + ]); + } + + for (const attr of decl.attributes.filter((attr) => attr.decl.ref && this.isPrismaAttribute(attr.decl.ref))) { this.generateModelAttribute(model, attr); } + + decl.attributes + .filter((attr) => attr.decl.ref && !this.isPrismaAttribute(attr.decl.ref)) + .forEach((attr) => model.addComment('/// ' + this.zModelGenerator.generateAttribute(attr))); + + // user defined comments pass-through + decl.comments.forEach((c) => model.addComment(c)); } private isPrismaAttribute(attr: Attribute) { @@ -194,23 +216,25 @@ export default class PrismaSchemaGenerator { private generateModelField(model: PrismaDataModel, field: DataModelField) { const fieldType = field.type.type || field.type.reference?.ref?.name; if (!fieldType) { - throw new GeneratorError( - `Field type is not resolved: ${field.$container.name}.${field.name}` - ); + throw new PluginError(`Field type is not resolved: ${field.$container.name}.${field.name}`); } - const type = new ModelFieldType( - fieldType, - field.type.array, - field.type.optional - ); + const type = new ModelFieldType(fieldType, field.type.array, field.type.optional); const attributes = field.attributes - .filter( - (attr) => attr.decl.ref && this.isPrismaAttribute(attr.decl.ref) - ) + .filter((attr) => attr.decl.ref && this.isPrismaAttribute(attr.decl.ref)) .map((attr) => this.makeFieldAttribute(attr)); - model.addField(field.name, type, attributes); + + const nonPrismaAttributes = field.attributes.filter( + (attr) => !attr.decl.ref || !this.isPrismaAttribute(attr.decl.ref) + ); + + const documentations = nonPrismaAttributes.map((attr) => '/// ' + this.zModelGenerator.generateAttribute(attr)); + + const result = model.addField(field.name, type, attributes, documentations); + + // user defined comments pass-through + field.comments.forEach((c) => result.addComment(c)); } private makeFieldAttribute(attr: DataModelFieldAttribute) { @@ -220,14 +244,11 @@ export default class PrismaSchemaGenerator { ); } - makeAttributeArg(arg: AttributeArg): PrismaAttributeArg { - return new PrismaAttributeArg( - arg.name, - this.makeAttributeArgValue(arg.value) - ); + private makeAttributeArg(arg: AttributeArg): PrismaAttributeArg { + return new PrismaAttributeArg(arg.name, this.makeAttributeArgValue(arg.value)); } - makeAttributeArgValue(node: Expression): PrismaAttributeArgValue { + private makeAttributeArgValue(node: Expression): PrismaAttributeArgValue { if (isLiteralExpr(node)) { switch (typeof node.value) { case 'string': @@ -237,40 +258,26 @@ export default class PrismaSchemaGenerator { case 'boolean': return new PrismaAttributeArgValue('Boolean', node.value); default: - throw new GeneratorError( - `Unexpected literal type: ${typeof node.value}` - ); + throw new PluginError(`Unexpected literal type: ${typeof node.value}`); } } else if (isArrayExpr(node)) { return new PrismaAttributeArgValue( 'Array', - new Array( - ...node.items.map((item) => - this.makeAttributeArgValue(item) - ) - ) + new Array(...node.items.map((item) => this.makeAttributeArgValue(item))) ); } else if (isReferenceExpr(node)) { return new PrismaAttributeArgValue( 'FieldReference', new PrismaFieldReference( resolved(node.target).name, - node.args.map( - (arg) => - new PrismaFieldReferenceArg(arg.name, arg.value) - ) + node.args.map((arg) => new PrismaFieldReferenceArg(arg.name, arg.value)) ) ); } else if (isInvocationExpr(node)) { // invocation - return new PrismaAttributeArgValue( - 'FunctionCall', - this.makeFunctionCall(node) - ); + return new PrismaAttributeArgValue('FunctionCall', this.makeFunctionCall(node)); } else { - throw new GeneratorError( - `Unsupported attribute argument expression type: ${node.$type}` - ); + throw new PluginError(`Unsupported attribute argument expression type: ${node.$type}`); } } @@ -279,19 +286,14 @@ export default class PrismaSchemaGenerator { resolved(node.function).name, node.args.map((arg) => { if (!isLiteralExpr(arg.value)) { - throw new GeneratorError( - 'Function call argument must be literal' - ); + throw new PluginError('Function call argument must be literal'); } return new PrismaFunctionCallArg(arg.name, arg.value.value); }) ); } - private generateModelAttribute( - model: PrismaDataModel, - attr: DataModelAttribute - ) { + private generateModelAttribute(model: PrismaDataModel, attr: DataModelAttribute) { model.attributes.push( new PrismaModelAttribute( resolved(attr.decl).name, diff --git a/packages/schema/src/plugins/prisma/zmodel-code-generator.ts b/packages/schema/src/plugins/prisma/zmodel-code-generator.ts new file mode 100644 index 000000000..4b25c7900 --- /dev/null +++ b/packages/schema/src/plugins/prisma/zmodel-code-generator.ts @@ -0,0 +1,160 @@ +import { + Argument, + ArrayExpr, + AttributeArg, + BinaryExpr, + BinaryExprOperatorPriority, + DataModelAttribute, + DataModelFieldAttribute, + Expression, + InvocationExpr, + LiteralExpr, + MemberAccessExpr, + NullExpr, + ReferenceArg, + ReferenceExpr, + ThisExpr, + UnaryExpr, +} from '@zenstackhq/language/ast'; +import { resolved } from '@zenstackhq/sdk'; + +/** + * Options for the generator. + */ +export interface ZModelCodeOptions { + binaryExprNumberOfSpaces: number; + unaryExprNumberOfSpaces: number; +} + +export default class ZModelCodeGenerator { + private readonly options: ZModelCodeOptions; + constructor(options?: Partial) { + this.options = { + binaryExprNumberOfSpaces: options?.binaryExprNumberOfSpaces ?? 1, + unaryExprNumberOfSpaces: options?.unaryExprNumberOfSpaces ?? 0, + }; + } + generateAttribute(ast: DataModelAttribute | DataModelFieldAttribute): string { + const args = ast.args.length ? `(${ast.args.map((x) => this.generateAttributeArg(x)).join(', ')})` : ''; + return `${resolved(ast.decl).name}${args}`; + } + + generateAttributeArg(ast: AttributeArg) { + if (ast.name) { + return `${ast.name}: ${this.generateExpression(ast.value)}`; + } else { + return this.generateExpression(ast.value); + } + } + + generateExpression(ast: Expression): string { + switch (ast.$type) { + case LiteralExpr: + return this.generateLiteralExpr(ast as LiteralExpr); + case UnaryExpr: + return this.generateUnaryExpr(ast as UnaryExpr); + case ArrayExpr: + return this.generateArrayExpr(ast as ArrayExpr); + case BinaryExpr: + return this.generateBinaryExpr(ast as BinaryExpr); + case ReferenceExpr: + return this.generateReferenceExpr(ast as ReferenceExpr); + case MemberAccessExpr: + return this.generateMemberExpr(ast as MemberAccessExpr); + case InvocationExpr: + return this.generateInvocationExpr(ast as InvocationExpr); + case NullExpr: + case ThisExpr: + return (ast as NullExpr | ThisExpr).value; + default: + throw new Error(`Not implemented: ${ast}`); + } + } + + generateArrayExpr(ast: ArrayExpr) { + return `[${ast.items.map((item) => this.generateExpression(item)).join(', ')}]`; + } + + generateLiteralExpr(ast: LiteralExpr) { + return typeof ast.value === 'string' ? `'${ast.value}'` : ast.value.toString(); + } + + generateUnaryExpr(ast: UnaryExpr) { + return `${ast.operator}${this.unaryExprSpace}${this.generateExpression(ast.operand)}`; + } + + generateBinaryExpr(ast: BinaryExpr) { + const operator = ast.operator; + const isCollectionPredicate = this.isCollectionPredicateOperator(operator); + const rightExpr = this.generateExpression(ast.right); + + const { left: isLeftParenthesis, right: isRightParenthesis } = this.isParenthesesNeededForBinaryExpr(ast); + + return `${isLeftParenthesis ? '(' : ''}${this.generateExpression(ast.left)}${isLeftParenthesis ? ')' : ''}${ + this.binaryExprSpace + }${operator}${this.binaryExprSpace}${isRightParenthesis ? '(' : ''}${ + isCollectionPredicate ? `[${rightExpr}]` : rightExpr + }${isRightParenthesis ? ')' : ''}`; + } + + generateReferenceExpr(ast: ReferenceExpr) { + const args = ast.args.length ? `(${ast.args.map((x) => this.generateReferenceArg(x)).join(', ')})` : ''; + return `${ast.target.ref?.name}${args}`; + } + + generateReferenceArg(ast: ReferenceArg) { + return `${ast.name}:${ast.value}`; + } + + generateMemberExpr(ast: MemberAccessExpr) { + return `${this.generateExpression(ast.operand)}.${ast.member.ref?.name}`; + } + + generateInvocationExpr(ast: InvocationExpr) { + return `${ast.function.ref?.name}(${ast.args.map((x) => this.generateArgument(x)).join(', ')})`; + } + + generateArgument(ast: Argument) { + return `${ast.name && ':'} ${this.generateExpression(ast.value)}`; + } + + private get binaryExprSpace(): string { + return ' '.repeat(this.options.binaryExprNumberOfSpaces); + } + + private get unaryExprSpace(): string { + return ' '.repeat(this.options.unaryExprNumberOfSpaces); + } + + private isParenthesesNeededForBinaryExpr(ast: BinaryExpr): { left: boolean; right: boolean } { + const result = { left: false, right: false }; + const operator = ast.operator; + const isCollectionPredicate = this.isCollectionPredicateOperator(operator); + + const currentPriority = BinaryExprOperatorPriority[operator]; + + if ( + ast.left.$type === BinaryExpr && + BinaryExprOperatorPriority[(ast.left as BinaryExpr)['operator']] < currentPriority + ) { + result.left = true; + } + /** + * 1 collection predicate operator has [] around the right operand, no need to add parenthesis. + * 2 grammar is left associative, so if the right operand has the same priority still need to add parenthesis. + **/ + if ( + !isCollectionPredicate && + ast.right.$type === BinaryExpr && + BinaryExprOperatorPriority[(ast.right as BinaryExpr)['operator']] <= currentPriority + ) { + result.right = true; + } + + return result; + } + + private isCollectionPredicateOperator(op: BinaryExpr['operator']) { + return ['?', '!', '^'].includes(op); + } +} diff --git a/packages/schema/src/res/package.template.json b/packages/schema/src/res/package.template.json deleted file mode 100644 index 0cc1fd604..000000000 --- a/packages/schema/src/res/package.template.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": ".zenstack", - "version": "1.0.0", - "description": "ZenStack generated code", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "author": "ZenStack", - "license": "MIT" -} diff --git a/packages/schema/src/res/prism-zmodel.js b/packages/schema/src/res/prism-zmodel.js index dd7c30d5a..2d9082332 100644 --- a/packages/schema/src/res/prism-zmodel.js +++ b/packages/schema/src/res/prism-zmodel.js @@ -2,13 +2,11 @@ (function (Prism) { Prism.languages.zmodel = Prism.languages.extend('clike', { - keyword: - /\b(?:datasource|enum|generator|model|attribute|function|null|this)\b/, + keyword: /\b(?:datasource|enum|generator|model|attribute|function|null|this)\b/, 'type-class-name': /(\b()\s+)[\w.\\]+/, }); - Prism.languages.javascript['class-name'][0].pattern = - /(\b(?:model|datasource|enum|generator)\s+)[\w.\\]+/; + Prism.languages.javascript['class-name'][0].pattern = /(\b(?:model|datasource|enum|generator)\s+)[\w.\\]+/; Prism.languages.insertBefore('zmodel', 'function', { annotation: { diff --git a/packages/schema/src/res/starter.zmodel b/packages/schema/src/res/starter.zmodel new file mode 100644 index 000000000..6cfee5b0f --- /dev/null +++ b/packages/schema/src/res/starter.zmodel @@ -0,0 +1,47 @@ +// This is a sample model to get you started. +// Learn how to model you app: https://zenstack.dev/#/modeling-your-app. + +/* + * A sample data source using local sqlite db. + * See how to use a different db: https://zenstack.dev/#/zmodel-data-source. + */ +datasource db { + provider = 'sqlite' + url = 'file:./todo.db' +} + +/* + * User model + */ +model User { + id String @id @default(cuid()) + email String @unique @email + password String @password @omit @length(8, 16) + posts Post[] + + // everybody can signup + @@allow('create', true) + + // full access by self + @@allow('all', auth() == this) +} + +/* + * Post model + */ +model Post { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String @length(1, 256) + content String + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId String? + + // allow read for all signin users + @@allow('read', auth() != null && published) + + // full access by author + @@allow('all', author == auth()) +} diff --git a/packages/schema/src/res/stdlib.zmodel b/packages/schema/src/res/stdlib.zmodel index 4a8078cb4..cd78b7a4f 100644 --- a/packages/schema/src/res/stdlib.zmodel +++ b/packages/schema/src/res/stdlib.zmodel @@ -84,8 +84,19 @@ function autoincrement(): Int {} */ function dbgenerated(expr: String): Any {} +/** + * Gets entities value before an update. Only valid when used in a "update" policy rule. + */ +function future(): Any {} + +/** + * Marks an attribute to be only applicable to certain field types. + */ attribute @@@targetField(targetField: AttributeTargetField[]) +/** + * Indicates an attribute is directly supported by the Prisma schema. + */ attribute @@@prisma() /* @@ -116,7 +127,7 @@ attribute @@index(_ fields: FieldReference[], map: String?) @@@prisma /* * Defines meta information about the relation. */ -attribute @relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?) @@@prisma +attribute @relation(_ name: String?, fields: FieldReference[]?, references: TransitiveFieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?) @@@prisma /* * Maps a field name or enum value from the schema to a column with a different name in the database. diff --git a/packages/schema/src/telemetry.ts b/packages/schema/src/telemetry.ts index b35a81299..9d4b9f66a 100644 --- a/packages/schema/src/telemetry.ts +++ b/packages/schema/src/telemetry.ts @@ -1,12 +1,13 @@ -import { Mixpanel, init } from 'mixpanel'; -import { TELEMETRY_TRACKING_TOKEN } from 'env'; -import { machineIdSync } from 'node-machine-id'; +import exitHook from 'async-exit-hook'; +import { CommanderError } from 'commander'; import cuid from 'cuid'; +import { init, Mixpanel } from 'mixpanel'; +import { machineIdSync } from 'node-machine-id'; import * as os from 'os'; import sleep from 'sleep-promise'; -import exitHook from 'async-exit-hook'; import { CliError } from './cli/cli-error'; -import { CommanderError } from 'commander'; +import { TELEMETRY_TRACKING_TOKEN } from './constants'; +import { getVersion } from './utils/version-utils'; /** * Telemetry events @@ -18,9 +19,9 @@ export type TelemetryEvents = | 'cli:command:start' | 'cli:command:complete' | 'cli:command:error' - | 'cli:generator:start' - | 'cli:generator:complete' - | 'cli:generator:error'; + | 'cli:plugin:start' + | 'cli:plugin:complete' + | 'cli:plugin:error'; /** * Utility class for sending telemetry @@ -29,15 +30,13 @@ export class Telemetry { private readonly mixpanel: Mixpanel | undefined; private readonly hostId = machineIdSync(); private readonly sessionid = cuid(); - private readonly trackingToken = TELEMETRY_TRACKING_TOKEN; private readonly _os = os.platform(); - // eslint-disable-next-line @typescript-eslint/no-var-requires - private readonly version = require('../package.json').version; + private readonly version = getVersion(); private exitWait = 200; constructor() { - if (process.env.DO_NOT_TRACK !== '1' && this.trackingToken) { - this.mixpanel = init(this.trackingToken, { + if (process.env.DO_NOT_TRACK !== '1' && TELEMETRY_TRACKING_TOKEN) { + this.mixpanel = init(TELEMETRY_TRACKING_TOKEN, { geolocate: true, }); } @@ -50,7 +49,7 @@ export class Telemetry { callback(); }); - exitHook.uncaughtExceptionHandler(async (err) => { + const errorHandler = async (err: Error) => { this.track('cli:error', { message: err.message, stack: err.stack, @@ -61,13 +60,16 @@ export class Telemetry { } if (err instanceof CliError || err instanceof CommanderError) { - // error already handled + // error already logged } else { - throw err; + console.error('\nAn unexpected error occurred:\n', err); } process.exit(1); - }); + }; + + exitHook.unhandledRejectionHandler(errorHandler); + exitHook.uncaughtExceptionHandler(errorHandler); } track(event: TelemetryEvents, properties: Record = {}) { diff --git a/packages/schema/src/generator/types.ts b/packages/schema/src/types.ts similarity index 56% rename from packages/schema/src/generator/types.ts rename to packages/schema/src/types.ts index 44f0f074b..436b2ec8b 100644 --- a/packages/schema/src/generator/types.ts +++ b/packages/schema/src/types.ts @@ -1,9 +1,9 @@ -import { Model } from '@lang/generated/ast'; +import { Model } from '@zenstackhq/language/ast'; export interface Context { schema: Model; + schemaPath: string; outDir: string; - generatedCodeDir: string; } export interface Generator { @@ -12,9 +12,3 @@ export interface Generator { get successMessage(): string; generate(context: Context): Promise; } - -export class GeneratorError extends Error { - constructor(message: string) { - super(message); - } -} diff --git a/packages/schema/src/utils/ast-utils.ts b/packages/schema/src/utils/ast-utils.ts new file mode 100644 index 000000000..ebde2e6c8 --- /dev/null +++ b/packages/schema/src/utils/ast-utils.ts @@ -0,0 +1,105 @@ +import { + DataModel, + DataModelAttribute, + Expression, + isDataModel, + isInvocationExpr, + Model, +} from '@zenstackhq/language/ast'; +import { PolicyOperationKind } from '@zenstackhq/runtime'; +import { getLiteral } from '@zenstackhq/sdk'; +import { isFromStdlib } from '../language-server/utils'; + +export function extractDataModelsWithAllowRules(model: Model): DataModel[] { + return model.declarations.filter( + (d) => isDataModel(d) && d.attributes.some((attr) => attr.decl.ref?.name === '@@allow') + ) as DataModel[]; +} + +export function analyzePolicies(dataModel: DataModel) { + const allows = dataModel.attributes.filter((attr) => attr.decl.ref?.name === '@@allow'); + const denies = dataModel.attributes.filter((attr) => attr.decl.ref?.name === '@@deny'); + + const create = toStaticPolicy('create', allows, denies); + const read = toStaticPolicy('read', allows, denies); + const update = toStaticPolicy('update', allows, denies); + const del = toStaticPolicy('delete', allows, denies); + const hasFieldValidation = dataModel.fields.some((field) => + field.attributes.some((attr) => VALIDATION_ATTRIBUTES.includes(attr.decl.$refText)) + ); + + return { + allows, + denies, + create, + read, + update, + delete: del, + allowAll: create === true && read === true && update === true && del === true, + denyAll: create === false && read === false && update === false && del === false, + hasFieldValidation, + }; +} + +function toStaticPolicy( + operation: PolicyOperationKind, + allows: DataModelAttribute[], + denies: DataModelAttribute[] +): boolean | undefined { + const filteredDenies = forOperation(operation, denies); + if (filteredDenies.some((rule) => getLiteral(rule.args[1].value) === true)) { + // any constant true deny rule + return false; + } + + const filteredAllows = forOperation(operation, allows); + if (filteredAllows.length === 0) { + // no allow rule + return false; + } + + if ( + filteredDenies.length === 0 && + filteredAllows.some((rule) => getLiteral(rule.args[1].value) === true) + ) { + // any constant true allow rule + return true; + } + return undefined; +} + +function forOperation(operation: PolicyOperationKind, rules: DataModelAttribute[]) { + return rules.filter((rule) => { + const ops = getLiteral(rule.args[0].value); + if (!ops) { + return false; + } + if (ops === 'all') { + return true; + } + const splitOps = ops.split(',').map((p) => p.trim()); + return splitOps.includes(operation); + }); +} + +export const VALIDATION_ATTRIBUTES = [ + '@length', + '@regex', + '@startsWith', + '@endsWith', + '@email', + '@url', + '@datetime', + '@gt', + '@gte', + '@lt', + '@lte', +]; + +export function getIdField(dataModel: DataModel) { + return dataModel.fields.find((f) => f.attributes.some((attr) => attr.decl.$refText === '@id')); +} + +export function isAuthInvocation(expr: Expression) { + return isInvocationExpr(expr) && expr.function.ref?.name === 'auth' && isFromStdlib(expr.function.ref); +} diff --git a/packages/schema/src/utils/exec-utils.ts b/packages/schema/src/utils/exec-utils.ts index 9022a1319..f355ae2b4 100644 --- a/packages/schema/src/utils/exec-utils.ts +++ b/packages/schema/src/utils/exec-utils.ts @@ -1,8 +1,9 @@ -import { execSync as _exec } from 'child_process'; +import { execSync as _exec, StdioOptions } from 'child_process'; /** * Utility for executing command synchronously and prints outputs on current console */ -export function execSync(cmd: string): void { - _exec(cmd, { encoding: 'utf-8', stdio: 'inherit' }); +export function execSync(cmd: string, stdio: StdioOptions = 'inherit', env?: Record): void { + const mergedEnv = { ...process.env, ...env }; + _exec(cmd, { encoding: 'utf-8', stdio, env: mergedEnv }); } diff --git a/packages/schema/src/utils/pkg-utils.ts b/packages/schema/src/utils/pkg-utils.ts index 9662cb59c..a1be85b98 100644 --- a/packages/schema/src/utils/pkg-utils.ts +++ b/packages/schema/src/utils/pkg-utils.ts @@ -18,33 +18,22 @@ export function installPackage( pkg: string, dev: boolean, pkgManager: PackageManagers | undefined = undefined, + tag = 'latest', projectPath = '.' ) { const manager = pkgManager ?? getPackageManager(projectPath); console.log(`Installing package "${pkg}" with ${manager}`); switch (manager) { case 'yarn': - execSync( - `yarn --cwd "${projectPath}" add ${pkg} ${ - dev ? ' --dev' : '' - } --ignore-engines` - ); + execSync(`yarn --cwd "${projectPath}" add ${pkg}@${tag} ${dev ? ' --dev' : ''} --ignore-engines`); break; case 'pnpm': - execSync( - `pnpm add -C "${projectPath}" ${ - dev ? ' --save-dev' : '' - } ${pkg}` - ); + execSync(`pnpm add -C "${projectPath}" ${dev ? ' --save-dev' : ''} ${pkg}@${tag}`); break; default: - execSync( - `npm install --prefix "${projectPath}" ${ - dev ? ' --save-dev' : '' - } ${pkg}` - ); + execSync(`npm install --prefix "${projectPath}" ${dev ? ' --save-dev' : ''} ${pkg}@${tag}`); break; } } diff --git a/packages/schema/src/utils/version-utils.ts b/packages/schema/src/utils/version-utils.ts new file mode 100644 index 000000000..a0b783c3b --- /dev/null +++ b/packages/schema/src/utils/version-utils.ts @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +export function getVersion() { + try { + return require('../package.json').version; + } catch { + return require('../../package.json').version; + } +} diff --git a/packages/schema/syntaxes/zmodel.json b/packages/schema/syntaxes/zmodel.json index f8bc39300..6d84ad7b6 100644 --- a/packages/schema/syntaxes/zmodel.json +++ b/packages/schema/syntaxes/zmodel.json @@ -1,57 +1,55 @@ { - "name": "zmodel", - "scopeName": "source.zmodel", - "fileTypes": [ - ".zmodel" - ], - "patterns": [ - { - "include": "#comments" - }, - { - "name": "keyword.control.zmodel", - "match": "\\b(Boolean|datasource|enum|model|String)\\b" - }, - { - "name": "string.quoted.double.zmodel", - "begin": "\"", - "end": "\"" - }, - { - "name": "string.quoted.single.zmodel", - "begin": "'", - "end": "'" - } - ], - "repository": { - "comments": { - "patterns": [ + "name": "zmodel", + "scopeName": "source.zmodel", + "fileTypes": [".zmodel"], + "patterns": [ + { + "include": "#comments" + }, { - "name": "comment.block.zmodel", - "begin": "/\\*", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.zmodel" - } - }, - "end": "\\*/", - "endCaptures": { - "0": { - "name": "punctuation.definition.comment.zmodel" - } - } + "name": "keyword.control.zmodel", + "match": "\\b(Boolean|datasource|enum|model|String)\\b" }, { - "begin": "//", - "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.zmodel" - } - }, - "end": "(?=$)", - "name": "comment.line.zmodel" + "name": "string.quoted.double.zmodel", + "begin": "\"", + "end": "\"" + }, + { + "name": "string.quoted.single.zmodel", + "begin": "'", + "end": "'" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.block.zmodel", + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + } + }, + { + "begin": "//", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.zmodel" + } + }, + "end": "(?=$)", + "name": "comment.line.zmodel" + } + ] } - ] } - } -} \ No newline at end of file +} diff --git a/packages/schema/syntaxes/zmodel.tmLanguage.json b/packages/schema/syntaxes/zmodel.tmLanguage.json index 433322aa6..f2f2fdfbb 100644 --- a/packages/schema/syntaxes/zmodel.tmLanguage.json +++ b/packages/schema/syntaxes/zmodel.tmLanguage.json @@ -1,57 +1,55 @@ { - "name": "zmodel", - "scopeName": "source.zmodel", - "fileTypes": [ - ".zmodel" - ], - "patterns": [ - { - "include": "#comments" - }, - { - "name": "keyword.control.zmodel", - "match": "\\b(Any|Asc|attribute|BigInt|Boolean|Bytes|ContextType|datasource|DateTime|Decimal|Desc|enum|FieldReference|Float|function|Int|Json|model|Null|sort|String)\\b" - }, - { - "name": "string.quoted.double.zmodel", - "begin": "\"", - "end": "\"" - }, - { - "name": "string.quoted.single.zmodel", - "begin": "'", - "end": "'" - } - ], - "repository": { - "comments": { - "patterns": [ + "name": "zmodel", + "scopeName": "source.zmodel", + "fileTypes": [".zmodel"], + "patterns": [ + { + "include": "#comments" + }, { - "name": "comment.block.zmodel", - "begin": "/\\*", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.zmodel" - } - }, - "end": "\\*/", - "endCaptures": { - "0": { - "name": "punctuation.definition.comment.zmodel" - } - } + "name": "keyword.control.zmodel", + "match": "\\b(Any|Asc|attribute|BigInt|Boolean|Bytes|ContextType|datasource|DateTime|Decimal|Desc|enum|FieldReference|Float|function|generator|Int|Json|model|Null|plugin|sort|String)\\b" }, { - "begin": "//", - "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.zmodel" - } - }, - "end": "(?=$)", - "name": "comment.line.zmodel" + "name": "string.quoted.double.zmodel", + "begin": "\"", + "end": "\"" + }, + { + "name": "string.quoted.single.zmodel", + "begin": "'", + "end": "'" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.block.zmodel", + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.zmodel" + } + } + }, + { + "begin": "//", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.zmodel" + } + }, + "end": "(?=$)", + "name": "comment.line.zmodel" + } + ] } - ] } - } -} \ No newline at end of file +} diff --git a/packages/schema/tests/cli/cli.test.ts b/packages/schema/tests/cli/cli.test.ts new file mode 100644 index 000000000..f42ee4846 --- /dev/null +++ b/packages/schema/tests/cli/cli.test.ts @@ -0,0 +1,77 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as tmp from 'tmp'; +import { createProgram } from '../../src/cli'; +import { execSync } from '../../src/utils/exec-utils'; + +describe('CLI Tests', () => { + let projDir: string; + let origDir: string; + + beforeEach(() => { + origDir = process.cwd(); + const r = tmp.dirSync(); + projDir = r.name; + console.log(`Project dir: ${projDir}`); + process.chdir(projDir); + }); + + afterEach(() => { + fs.rmSync(projDir, { recursive: true, force: true }); + process.chdir(origDir); + }); + + function createNpmrc() { + fs.writeFileSync('.npmrc', `cache=${path.join(__dirname, '../.npmcache')}`); + } + + it('init project t3 std', async () => { + execSync('npx --yes create-t3-app@latest --prisma --CI --noGit .', 'inherit', { + npm_config_user_agent: 'npm', + npm_config_cache: path.join(__dirname, '../.npmcache'), + }); + createNpmrc(); + + const program = createProgram(); + program.parse(['init', '--tag', 'canary'], { from: 'user' }); + + expect(fs.readFileSync('schema.zmodel', 'utf-8')).toEqual(fs.readFileSync('prisma/schema.prisma', 'utf-8')); + }); + + it('init project t3 non-std prisma schema', async () => { + execSync('npx --yes create-t3-app@latest --prisma --CI --noGit .', 'inherit', { + npm_config_user_agent: 'npm', + npm_config_cache: path.join(__dirname, '../.npmcache'), + }); + createNpmrc(); + fs.renameSync('prisma/schema.prisma', 'prisma/my.prisma'); + + const program = createProgram(); + program.parse(['init', '--tag', 'canary', '--prisma', 'prisma/my.prisma'], { from: 'user' }); + + expect(fs.readFileSync('schema.zmodel', 'utf-8')).toEqual(fs.readFileSync('prisma/my.prisma', 'utf-8')); + }); + + it('init project empty project', async () => { + fs.writeFileSync('package.json', JSON.stringify({ name: 'my app', version: '1.0.0' })); + createNpmrc(); + const program = createProgram(); + program.parse(['init', '--tag', 'canary'], { from: 'user' }); + expect(fs.readFileSync('schema.zmodel', 'utf-8')).toBeTruthy(); + }); + + it('init project existing zmodel', async () => { + fs.writeFileSync('package.json', JSON.stringify({ name: 'my app', version: '1.0.0' })); + const origZModelContent = ` + datasource db { + provider = 'sqlite' + url = 'file:./todo.db' + } + `; + fs.writeFileSync('schema.zmodel', origZModelContent); + createNpmrc(); + const program = createProgram(); + program.parse(['init', '--tag', 'canary'], { from: 'user' }); + expect(fs.readFileSync('schema.zmodel', 'utf-8')).toEqual(origZModelContent); + }); +}); diff --git a/packages/schema/tests/generator/code-generator.test.ts b/packages/schema/tests/generator/code-generator.test.ts new file mode 100644 index 000000000..6df2144bb --- /dev/null +++ b/packages/schema/tests/generator/code-generator.test.ts @@ -0,0 +1,105 @@ +import { loadModel } from '../utils'; +import ZModelCodeGenerator from '../../src/plugins/prisma/zmodel-code-generator'; +import { DataModel, DataModelAttribute, DataModelFieldAttribute } from '@zenstackhq/language/ast'; + +describe('Code Generator Tests', () => { + const generator = new ZModelCodeGenerator(); + + function checkAttribute(ast: DataModelAttribute | DataModelFieldAttribute, expected: string) { + const result = generator.generateAttribute(ast); + expect(result).toBe(expected); + } + + async function getModule(schema: string) { + if (!schema.includes('datasource ')) { + schema = + ` + datasource db { + provider = 'postgresql' + url = 'dummy' + } + ` + schema; + } + + return loadModel(schema); + } + + async function getModelDeclaration(schema: string, name: string) { + const module = await getModule(schema); + return module.declarations.find((d) => d.name === name) as DataModel; + } + + it('check field attribute', async () => { + const model = await getModelDeclaration( + ` + model Test{ + id String @id @length(4, 50) @regex('^[0-9a-zA-Z]{4,16}$') + } + `, + 'Test' + ); + + checkAttribute(model.fields[0].attributes[0], '@id'); + checkAttribute(model.fields[0].attributes[1], '@length(4, 50)'); + checkAttribute(model.fields[0].attributes[2], "@regex('^[0-9a-zA-Z]{4,16}$')"); + }); + + it('check basic model attribute', async () => { + const model = await getModelDeclaration( + ` + model User { + id String @id + + @@deny('all', auth() == null) + @@allow('create', true) + } + `, + 'User' + ); + + checkAttribute(model.attributes[0], `@@deny('all', auth() == null)`); + checkAttribute(model.attributes[1], `@@allow('create', true)`); + }); + + it('check collection expression', async () => { + const model = await getModelDeclaration( + ` + enum UserRole { + USER + ADMIN + } + + model User { + id String @id + name String + role UserRole + deleted Boolean + level Int + + + posts Post[] + + @@allow('read', posts ? [author == auth()]) + + @@deny('read', name == '123' && (role == USER || name == '456')) + + @@allow('delete', posts?[author == auth() && ( level <10 || author.role == USER) && !author.deleted]) + } + + model Post { + id String @id + author User? @relation(fields: [authorId], references: [id]) + authorId String? + } + `, + 'User' + ); + + checkAttribute(model.attributes[0], `@@allow('read', posts ? [author == auth()])`); + checkAttribute(model.attributes[1], `@@deny('read', name == '123' && (role == USER || name == '456'))`); + checkAttribute( + model.attributes[2], + `@@allow('delete', posts ? [author == auth() && (level < 10 || author.role == USER) && !author.deleted])` + ); + }); +}); diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts index ca4e66d8a..79e22174f 100644 --- a/packages/schema/tests/generator/expression-writer.test.ts +++ b/packages/schema/tests/generator/expression-writer.test.ts @@ -1,15 +1,9 @@ +import { DataModel, Enum, Expression, isDataModel, isEnum } from '@zenstackhq/language/ast'; +import { GUARD_FIELD_NAME } from '@zenstackhq/sdk'; +import * as tmp from 'tmp'; import { Project, VariableDeclarationKind } from 'ts-morph'; -import { - DataModel, - Enum, - Expression, - isDataModel, - isEnum, -} from '../../src/language-server/generated/ast'; +import { ExpressionWriter } from '../../src/plugins/access-policy/expression-writer'; import { loadModel } from '../utils'; -import * as tmp from 'tmp'; -import { GUARD_FIELD_NAME } from '../../src/generator/constants'; -import expressionWriter from '../../src/generator/prisma/expression-writer'; describe('Expression Writer Tests', () => { it('boolean literal', async () => { @@ -125,9 +119,11 @@ describe('Expression Writer Tests', () => { } `, (model) => model.attributes[0].args[1].value, - `{ + `!user ? + { zenstack_guard: false } : + { id: { - equals: user?.id + equals: (user ? user.id: null) } }` ); @@ -141,10 +137,12 @@ describe('Expression Writer Tests', () => { } `, (model) => model.attributes[0].args[1].value, - `{ + `!user ? + { zenstack_guard: false } : + { id: { not: { - equals: user?.id + equals: (user ? user.id: null) } } }` @@ -326,7 +324,7 @@ describe('Expression Writer Tests', () => { foo: { is: { x : { - le: 0 + lte: 0 } } } @@ -419,7 +417,7 @@ describe('Expression Writer Tests', () => { bar: { is: { x : { - le: 0 + lte: 0 } } } @@ -450,7 +448,7 @@ describe('Expression Writer Tests', () => { foos: { some: { x: { - le: 0 + lte: 0 } } } @@ -477,7 +475,7 @@ describe('Expression Writer Tests', () => { foos: { every: { x: { - le: 0 + lte: 0 } } } @@ -504,7 +502,7 @@ describe('Expression Writer Tests', () => { foos: { none: { x: { - le: 0 + lte: 0 } } } @@ -540,7 +538,7 @@ describe('Expression Writer Tests', () => { bars: { some: { x: { - le: 0 + lte: 0 } } } @@ -592,15 +590,18 @@ describe('Expression Writer Tests', () => { } `, (model) => model.attributes[0].args[1].value, - `{ - owner: { - is: { - id: { - equals: user?.id + `!user ? + { zenstack_guard : false } : + { + owner: { + is: { + id: { + equals: (user ? user.id : null) + } } } } - }` + ` ); await check( @@ -618,12 +619,14 @@ describe('Expression Writer Tests', () => { } `, (model) => model.attributes[0].args[1].value, - `{ + `!user ? + { zenstack_guard : false } : + { owner: { is: { id: { not: { - equals: user?.id + equals: (user ? user.id : null) } } } @@ -646,11 +649,13 @@ describe('Expression Writer Tests', () => { } `, (model) => model.attributes[0].args[1].value, - `{ + `!user ? + { zenstack_guard : false } : + { owner: { is: { id: { - equals: user?.id + equals: (user ? user.id : null) } } } @@ -659,11 +664,7 @@ describe('Expression Writer Tests', () => { }); }); -async function check( - schema: string, - getExpr: (model: DataModel) => Expression, - expected: string -) { +async function check(schema: string, getExpr: (model: DataModel) => Expression, expected: string) { if (!schema.includes('datasource ')) { schema = ` @@ -675,11 +676,7 @@ async function check( } const model = await loadModel(schema); - const expr = getExpr( - model.declarations.find( - (d) => isDataModel(d) && d.name === 'Test' - ) as DataModel - ); + const expr = getExpr(model.declarations.find((d) => isDataModel(d) && d.name === 'Test') as DataModel); const project = new Project(); @@ -705,9 +702,7 @@ async function check( name: e.name, initializer: ` { - ${(e as Enum).fields - .map((f) => `${f.name}: "${f.name}"`) - .join(',\n')} + ${(e as Enum).fields.map((f) => `${f.name}: "${f.name}"`).join(',\n')} } `, }, @@ -720,8 +715,7 @@ async function check( declarations: [ { name: 'expr', - initializer: (writer) => - new expressionWriter(writer).write(expr), + initializer: (writer) => new ExpressionWriter(writer).write(expr), }, ], }); @@ -741,9 +735,7 @@ async function check( // console.log('Generated expr:\n', outExpr?.getText()); if (expected) { - const generatedExpr = outExpr!.getInitializer()!.getText(); - expect(generatedExpr.replace(/\s+/g, '')).toBe( - expected.replace(/\s+/g, '') - ); + const generatedExpr = outExpr?.getInitializer()?.getText(); + expect(generatedExpr && generatedExpr.replace(/\s+/g, '')).toBe(expected.replace(/\s+/g, '')); } } diff --git a/packages/schema/tests/generator/prisma-builder.test.ts b/packages/schema/tests/generator/prisma-builder.test.ts index 1a108ed18..a8d1276b2 100644 --- a/packages/schema/tests/generator/prisma-builder.test.ts +++ b/packages/schema/tests/generator/prisma-builder.test.ts @@ -1,15 +1,15 @@ +import { getDMMF } from '@prisma/internals'; import { - DataSourceUrl, - PrismaModel, - FieldAttribute, AttributeArg, - FunctionCall, AttributeArgValue, - ModelFieldType, + DataSourceUrl, + FieldAttribute, FieldReference, FieldReferenceArg, -} from '../../src/generator/prisma/prisma-builder'; -import { getDMMF } from '@prisma/internals'; + FunctionCall, + ModelFieldType, + PrismaModel, +} from '../../src/plugins/prisma/prisma-builder'; async function validate(model: PrismaModel) { const content = model.toString(); @@ -21,61 +21,47 @@ async function validate(model: PrismaModel) { } } -// TODO: this test suite is failing on github actions; disabling for now -describe.skip('Prisma Builder Tests', () => { +describe('Prisma Builder Tests', () => { it('datasource', async () => { let model = new PrismaModel(); - model.addDataSource( - 'db', - 'postgresql', - new DataSourceUrl('DATABASE_URL', true) - ); + model.addDataSource('db', 'postgresql', new DataSourceUrl('DATABASE_URL', true)); await validate(model); model = new PrismaModel(); model.addDataSource( 'db', 'postgresql', - new DataSourceUrl( - 'postgresql://postgres:abc123@localhost:5432/sample?schema=public', - false - ) + new DataSourceUrl('postgresql://postgres:abc123@localhost:5432/sample?schema=public', false) ); await validate(model); }); it('enum', async () => { - let model = new PrismaModel(); + const model = new PrismaModel(); model.addEnum('UserRole', ['USER', 'ADMIN']); await validate(model); }); it('generator', async () => { - let model = new PrismaModel(); - model.addGenerator('client', 'prisma-client-js', '.prisma'); + const model = new PrismaModel(); + model.addGenerator('client', [{ name: 'provider', value: 'prisma-client-js' }]); await validate(model); }); it('model', async () => { - let model = new PrismaModel(); + const model = new PrismaModel(); const dm = model.addModel('User'); dm.addField('id', 'String', [new FieldAttribute('@id')]); dm.addField('createdAt', 'DateTime', [ new FieldAttribute('@default', [ - new AttributeArg( - undefined, - new AttributeArgValue( - 'FunctionCall', - new FunctionCall('now') - ) - ), + new AttributeArg(undefined, new AttributeArgValue('FunctionCall', new FunctionCall('now'))), ]), ]); await validate(model); }); it('relation', async () => { - let model = new PrismaModel(); + const model = new PrismaModel(); const user = model.addModel('User'); user.addField('id', 'String', [new FieldAttribute('@id')]); user.addField('posts', new ModelFieldType('Post', true)); @@ -86,23 +72,13 @@ describe.skip('Prisma Builder Tests', () => { new FieldAttribute('@relation', [ new AttributeArg( 'fields', - new AttributeArgValue('Array', [ - new AttributeArgValue('FieldReference', 'userId'), - ]) + new AttributeArgValue('Array', [new AttributeArgValue('FieldReference', 'userId')]) ), new AttributeArg( 'references', - new AttributeArgValue('Array', [ - new AttributeArgValue('FieldReference', 'id'), - ]) - ), - new AttributeArg( - 'onDelete', - new AttributeArgValue( - 'FieldReference', - new FieldReference('Cascade') - ) + new AttributeArgValue('Array', [new AttributeArgValue('FieldReference', 'id')]) ), + new AttributeArg('onDelete', new AttributeArgValue('FieldReference', new FieldReference('Cascade'))), ]), ]); post.addField('userId', 'String'); @@ -111,7 +87,7 @@ describe.skip('Prisma Builder Tests', () => { }); it('model attribute', async () => { - let model = new PrismaModel(); + const model = new PrismaModel(); const post = model.addModel('Post'); post.addField('id', 'String', [new FieldAttribute('@id')]); post.addField('slug', 'String'); @@ -120,15 +96,10 @@ describe.skip('Prisma Builder Tests', () => { new AttributeArg( 'fields', new AttributeArgValue('Array', [ + new AttributeArgValue('FieldReference', new FieldReference('space')), new AttributeArgValue( 'FieldReference', - new FieldReference('space') - ), - new AttributeArgValue( - 'FieldReference', - new FieldReference('slug', [ - new FieldReferenceArg('sort', 'Desc'), - ]) + new FieldReference('slug', [new FieldReferenceArg('sort', 'Desc')]) ), ]) ), diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts new file mode 100644 index 000000000..55bde0023 --- /dev/null +++ b/packages/schema/tests/generator/prisma-generator.test.ts @@ -0,0 +1,64 @@ +import fs from 'fs'; +import tmp from 'tmp'; +import PrismaSchemaGenerator from '../../src/plugins/prisma/schema-generator'; +import { loadModel } from '../utils'; + +describe('Prisma generator test', () => { + it('triple slash comments', async () => { + const model = await loadModel(` + datasource db { + provider = 'sqlite' + url = 'file:dev.db' + } + + /// This is a comment + model Foo { + id String @id + /// Comment for field value + value Int + } + `); + + const { name } = tmp.fileSync({ postfix: '.prisma' }); + await new PrismaSchemaGenerator().generate(model, { + provider: '@zenstack/prisma', + schemaPath: 'schema.zmodel', + output: name, + }); + + const content = fs.readFileSync(name, 'utf-8'); + expect(content).toContain('/// This is a comment'); + expect(content).toContain('/// Comment for field value'); + }); + + it('triple slash attribute pass-through', async () => { + const model = await loadModel(` + datasource db { + provider = 'sqlite' + url = 'file:dev.db' + } + + attribute @TypeGraphQL.omit(output: Any?, input: Any?) + attribute @TypeGraphQL.field(name: String) + + model User { + id Int @id + password String @TypeGraphQL.omit(output: true, input: true) + another String @TypeGraphQL.omit(input: ['update', 'where', 'orderBy']) + foo String @TypeGraphQL.field(name: 'bar') + } + `); + + const { name } = tmp.fileSync({ postfix: '.prisma' }); + await new PrismaSchemaGenerator().generate(model, { + provider: '@zenstack/prisma', + schemaPath: 'schema.zmodel', + output: name, + }); + + const content = fs.readFileSync(name, 'utf-8'); + expect(content).toContain(`/// @TypeGraphQL.omit(output: true, input: true)`); + expect(content).toContain(`/// @TypeGraphQL.omit(input: ['update', 'where', 'orderBy'])`); + expect(content).toContain(`/// @TypeGraphQL.field(name: 'bar')`); + }); +}); diff --git a/packages/schema/tests/schema/formatter.test.ts b/packages/schema/tests/schema/formatter.test.ts new file mode 100644 index 000000000..4de78fc4e --- /dev/null +++ b/packages/schema/tests/schema/formatter.test.ts @@ -0,0 +1,36 @@ +import { EmptyFileSystem } from 'langium'; +import { expectFormatting } from 'langium/test'; +import { createZModelServices } from '../../src/language-server/zmodel-module'; +const services = createZModelServices({ ...EmptyFileSystem }).ZModel; +const formatting = expectFormatting(services); + +describe('ZModelFormatter', () => { + it('declaration formatting', async () => { + await formatting({ + before: `datasource db { provider = 'postgresql' url = env('DATABASE_URL')} generator js {provider = 'prisma-client-js'} + plugin reactHooks {provider = '@zenstackhq/react'output = 'lib/hooks'} + model User {id:id String @id name String? } + enum Role {ADMIN USER}`, + after: `datasource db { + provider = 'postgresql' + url = env('DATABASE_URL') +} +generator js { + provider = 'prisma-client-js' +} +plugin reactHooks { + provider = '@zenstackhq/react' + output = 'lib/hooks' +} +model User { + id + id String @id + name String? +} +enum Role { + ADMIN + USER +}`, + }); + }); +}); diff --git a/packages/schema/tests/schema/parser.test.ts b/packages/schema/tests/schema/parser.test.ts index 8f65a2ff0..83daa8af3 100644 --- a/packages/schema/tests/schema/parser.test.ts +++ b/packages/schema/tests/schema/parser.test.ts @@ -1,16 +1,17 @@ +/* eslint-disable @typescript-eslint/ban-types */ import { + ArrayExpr, + AttributeArg, BinaryExpr, - LiteralExpr, - InvocationExpr, - DataSource, DataModel, - Function, - AttributeArg, + DataSource, Enum, - UnaryExpr, + FunctionDecl, + InvocationExpr, + LiteralExpr, ReferenceExpr, - ArrayExpr, -} from '../../src/language-server/generated/ast'; + UnaryExpr, +} from '@zenstackhq/language/ast'; import { loadModel } from '../utils'; describe('Parsing Tests', () => { @@ -35,12 +36,8 @@ describe('Parsing Tests', () => { }) ); expect(ds.fields[1].name).toBe('url'); - expect((ds.fields[1].value as InvocationExpr).function.ref?.name).toBe( - 'env' - ); - expect((ds.fields[1].value as InvocationExpr).args[0].value.$type).toBe( - LiteralExpr - ); + expect((ds.fields[1].value as InvocationExpr).function.ref?.name).toBe('env'); + expect((ds.fields[1].value as InvocationExpr).args[0].value.$type).toBe(LiteralExpr); }); it('enum', async () => { @@ -58,9 +55,7 @@ describe('Parsing Tests', () => { const doc = await loadModel(content, false); const enumDecl = doc.declarations[0]; expect(enumDecl.name).toBe('UserRole'); - expect((enumDecl as Enum).fields.map((f) => f.name)).toEqual( - expect.arrayContaining(['USER', 'ADMIN']) - ); + expect((enumDecl as Enum).fields.map((f) => f.name)).toEqual(expect.arrayContaining(['USER', 'ADMIN'])); const model = doc.declarations[1] as DataModel; expect(model.fields[1].type.reference?.ref?.name).toBe('UserRole'); @@ -124,9 +119,7 @@ describe('Parsing Tests', () => { const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; expect(model.fields[0].attributes[0].decl.ref?.name).toBe('@id'); - expect(model.fields[1].attributes[0].args[0].value.$type).toBe( - LiteralExpr - ); + expect(model.fields[1].attributes[0].args[0].value.$type).toBe(LiteralExpr); expect(model.fields[1].attributes[1].decl.ref?.name).toBe('@unique'); }); @@ -151,25 +144,15 @@ describe('Parsing Tests', () => { ) ).toEqual(expect.arrayContaining(['a', 'b'])); - expect( - ( - (model.attributes[1].args[0].value as ArrayExpr) - .items[0] as ReferenceExpr - ).args[0] - ).toEqual( + expect(((model.attributes[1].args[0].value as ArrayExpr).items[0] as ReferenceExpr).args[0]).toEqual( expect.objectContaining({ name: 'sort', value: 'Asc', }) ); - expect( - (model.attributes[2].args[0].value as ReferenceExpr).target.ref - ?.name - ).toBe('b'); - expect( - (model.attributes[2].args[0].value as ReferenceExpr).args[0] - ).toEqual( + expect((model.attributes[2].args[0].value as ReferenceExpr).target.ref?.name).toBe('b'); + expect((model.attributes[2].args[0].value as ReferenceExpr).args[0]).toEqual( expect.objectContaining({ name: 'sort', value: 'Desc', @@ -191,8 +174,8 @@ describe('Parsing Tests', () => { `; const doc = await loadModel(content, false); const models = doc.declarations as DataModel[]; - expect(models[0].fields[1].type.reference?.ref?.name === 'Post'); - expect(models[1].fields[1].type.reference?.ref?.name === 'User'); + expect(models[0].fields[1].type.reference?.ref?.name).toBe('Post'); + expect(models[1].fields[1].type.reference?.ref?.name).toBe('User'); }); it('policy expressions', async () => { @@ -213,25 +196,15 @@ describe('Parsing Tests', () => { const attrs = model.attributes; expect(attrs[0].args[1].value.$type).toBe(UnaryExpr); - expect((attrs[0].args[1].value as UnaryExpr).operand.$type).toBe( - ReferenceExpr - ); + expect((attrs[0].args[1].value as UnaryExpr).operand.$type).toBe(ReferenceExpr); expect(attrs[1].args[1].value.$type).toBe(BinaryExpr); - expect((attrs[1].args[1].value as BinaryExpr).left.$type).toBe( - ReferenceExpr - ); - expect((attrs[1].args[1].value as BinaryExpr).right.$type).toBe( - LiteralExpr - ); + expect((attrs[1].args[1].value as BinaryExpr).left.$type).toBe(ReferenceExpr); + expect((attrs[1].args[1].value as BinaryExpr).right.$type).toBe(LiteralExpr); expect(attrs[1].args[1].value.$type).toBe(BinaryExpr); - expect((attrs[1].args[1].value as BinaryExpr).left.$type).toBe( - ReferenceExpr - ); - expect((attrs[1].args[1].value as BinaryExpr).right.$type).toBe( - LiteralExpr - ); + expect((attrs[1].args[1].value as BinaryExpr).left.$type).toBe(ReferenceExpr); + expect((attrs[1].args[1].value as BinaryExpr).right.$type).toBe(LiteralExpr); // expect(attrs[2].args[0].value.$type).toBe(BinaryExpr); // expect((attrs[2].args[0].value as BinaryExpr).left.$type).toBe( @@ -365,13 +338,11 @@ describe('Parsing Tests', () => { `; const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; - const foo = doc.declarations[2] as Function; - const bar = doc.declarations[3] as Function; + const foo = doc.declarations[2] as FunctionDecl; + const bar = doc.declarations[3] as FunctionDecl; expect(foo.name).toBe('foo'); - expect(foo.params.map((p) => p.type.type)).toEqual( - expect.arrayContaining(['Int', 'Int']) - ); + expect(foo.params.map((p) => p.type.type)).toEqual(expect.arrayContaining(['Int', 'Int'])); expect(bar.name).toBe('bar'); expect(bar.params[0].type.reference?.ref?.name).toBe('N'); diff --git a/packages/schema/tests/schema/sample-todo.test.ts b/packages/schema/tests/schema/sample-todo.test.ts index 8700b6580..721103891 100644 --- a/packages/schema/tests/schema/sample-todo.test.ts +++ b/packages/schema/tests/schema/sample-todo.test.ts @@ -1,14 +1,12 @@ -import { loadModel } from '../utils'; import * as fs from 'fs'; +import path from 'path'; +import { loadModel } from '../utils'; describe('Basic Tests', () => { it('sample todo schema', async () => { - const content = fs.readFileSync( - '../../samples/todo/zenstack/schema.zmodel', - { - encoding: 'utf-8', - } - ); + const content = fs.readFileSync(path.join(__dirname, './todo.zmodel'), { + encoding: 'utf-8', + }); await loadModel(content); }); }); diff --git a/packages/schema/tests/schema/stdlib.test.ts b/packages/schema/tests/schema/stdlib.test.ts index f6672463c..e4fa5b966 100644 --- a/packages/schema/tests/schema/stdlib.test.ts +++ b/packages/schema/tests/schema/stdlib.test.ts @@ -14,23 +14,17 @@ describe('Stdlib Tests', () => { validationChecks: 'all', }); - const validationErrors = (stdLib.diagnostics ?? []).filter( - (e) => e.severity === 1 - ); + const validationErrors = (stdLib.diagnostics ?? []).filter((e) => e.severity === 1); if (validationErrors.length > 0) { for (const validationError of validationErrors) { - const range = stdLib.textDocument.getText( - validationError.range - ); + const range = stdLib.textDocument.getText(validationError.range); console.error( - `line ${validationError.range.start.line + 1}: ${ - validationError.message - }${range ? ' [' + range + ']' : ''}` + `line ${validationError.range.start.line + 1}: ${validationError.message}${ + range ? ' [' + range + ']' : '' + }` ); } - throw new SchemaLoadingError( - validationErrors.map((e) => e.message) - ); + throw new SchemaLoadingError(validationErrors.map((e) => e.message)); } }); }); diff --git a/samples/todo/zenstack/schema.zmodel b/packages/schema/tests/schema/todo.zmodel similarity index 85% rename from samples/todo/zenstack/schema.zmodel rename to packages/schema/tests/schema/todo.zmodel index 8ec9fd717..d08b3049b 100644 --- a/samples/todo/zenstack/schema.zmodel +++ b/packages/schema/tests/schema/todo.zmodel @@ -3,13 +3,22 @@ */ /* - * Data soruce definition + * Data source definition */ datasource db { provider = 'postgresql' url = env('DATABASE_URL') } +generator js { + provider = 'prisma-client-js' +} + +plugin reactHooks { + provider = '@zenstackhq/react' + output = 'lib/hooks' +} + /* * Enum for user's role in a space */ @@ -55,7 +64,6 @@ model SpaceUser { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String role SpaceUserRole - @@unique([userId, spaceId]) // require login @@ -94,7 +102,7 @@ model User { @@allow('read', spaces?[space.members?[user == auth()]]) // full access by oneself - @@allow('all', auth() == this) + @@allow('all', auth() == this) } /* @@ -118,11 +126,15 @@ model List { // can be read by owner or space members (only if not private) @@allow('read', owner == auth() || (space.members?[user == auth()] && !private)) - // when create/udpate, owner must be set to current user, and user must be in the space - @@allow('create,update', owner == auth() && space.members?[user == auth()]) + // when create, owner must be set to current user, and user must be in the space + @@allow('create', owner == auth() && space.members?[user == auth()]) + + // when create, owner must be set to current user, and user must be in the space + // update is not allowed to change owner + @@allow('update', owner == auth() && space.members?[user == auth()] && future().owner == owner) // can be deleted by owner - @@allow('delete', owner == auth()) + @@allow('delete', owner == auth()) } /* @@ -145,6 +157,9 @@ model Todo { // owner has full access, also space members have full access (if the parent List is not private) @@allow('all', list.owner == auth()) @@allow('all', list.space.members?[user == auth()] && !list.private) + + // update cannot change owner + @@deny('update', future().owner != owner) } // next-auth @@ -163,6 +178,5 @@ model Account { id_token String? session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@unique([provider, providerAccountId]) } \ No newline at end of file diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts index 5fc04625c..e713a43ae 100644 --- a/packages/schema/tests/schema/validation/attribute-validation.test.ts +++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts @@ -80,9 +80,7 @@ describe('Attribute tests', () => { id String @id() @default(foo: 'abc') } `) - ).toContain( - `Attribute "@default" doesn't have a parameter named "foo"` - ); + ).toContain(`Attribute "@default" doesn't have a parameter named "foo"`); }); it('field attribute coverage', async () => { @@ -158,9 +156,7 @@ describe('Attribute tests', () => { @@unique([x, z]) } `) - ).toContain( - `Could not resolve reference to ReferenceTarget named 'z'.` - ); + ).toContain(`Could not resolve reference to ReferenceTarget named 'z'.`); await loadModel(` ${prelude} @@ -207,7 +203,7 @@ describe('Attribute tests', () => { id String @id @default(foo()) } `) - ).toContain(`Could not resolve reference to Function named 'foo'.`); + ).toContain(`Could not resolve reference to FunctionDecl named 'foo'.`); expect( await loadModelWithError(` @@ -229,9 +225,7 @@ describe('Attribute tests', () => { @@allow('all', auth() != null) } `) - ).toContain( - `auth() cannot be resolved because no "User" model is defined` - ); + ).toContain(`auth() cannot be resolved because no "User" model is defined`); await loadModel(` ${prelude} @@ -261,9 +255,7 @@ describe('Attribute tests', () => { @@allow('all', auth().email != null) } `) - ).toContain( - `Could not resolve reference to DataModelField named 'email'.` - ); + ).toContain(`Could not resolve reference to DataModelField named 'email'.`); }); it('collection predicate expression check', async () => { @@ -282,9 +274,7 @@ describe('Attribute tests', () => { @@allow('all', a?[x > 0]) } `) - ).toContain( - `collection predicate can only be used on an array of model type` - ); + ).toContain(`collection predicate can only be used on an array of model type`); await loadModel(` ${prelude} diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts index ff386b68a..a5fb148f5 100644 --- a/packages/schema/tests/schema/validation/datamodel-validation.test.ts +++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts @@ -48,9 +48,7 @@ describe('Data Model Validation Tests', () => { x Int[]? } `) - ).toContain( - 'Optional lists are not supported. Use either `Type[]` or `Type?`' - ); + ).toContain('Optional lists are not supported. Use either `Type[]` or `Type?`'); }); it('unresolved field type', async () => { @@ -62,9 +60,7 @@ describe('Data Model Validation Tests', () => { x Integer } `) - ).toContain( - `Could not resolve reference to TypeDeclaration named 'Integer'.` - ); + ).toContain(`Could not resolve reference to TypeDeclaration named 'Integer'.`); expect( await loadModelWithError(` @@ -74,9 +70,7 @@ describe('Data Model Validation Tests', () => { x Integer[] } `) - ).toContain( - `Could not resolve reference to TypeDeclaration named 'Integer'.` - ); + ).toContain(`Could not resolve reference to TypeDeclaration named 'Integer'.`); expect( await loadModelWithError(` @@ -86,17 +80,43 @@ describe('Data Model Validation Tests', () => { x Integer? } `) - ).toContain( - `Could not resolve reference to TypeDeclaration named 'Integer'.` - ); + ).toContain(`Could not resolve reference to TypeDeclaration named 'Integer'.`); }); it('id field', async () => { + // no need for '@id' field when there's no access policy or field validation + await loadModel(` + ${prelude} + model M { + x Int + } + `); + expect( await loadModelWithError(` ${prelude} model M { x Int + @@allow('all', x > 0) + } + `) + ).toContain(`Model must include a field with @id attribute`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int + @@deny('all', x <= 0) + } + `) + ).toContain(`Model must include a field with @id attribute`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int @gt(0) } `) ).toContain(`Model must include a field with @id attribute`); @@ -195,9 +215,7 @@ describe('Data Model Validation Tests', () => { id String @id } `) - ).toContain( - `The relation field "b" on model "A" is missing an opposite relation field on model "B"` - ); + ).toContain(`The relation field "b" on model "A" is missing an opposite relation field on model "B"`); // one-to-one ambiguous expect( @@ -214,9 +232,24 @@ describe('Data Model Validation Tests', () => { a1 A } `) - ).toContain( - `Fields "a", "a1" on model "B" refer to the same relation to model "A"` - ); + ).toContain(`Fields "a", "a1" on model "B" refer to the same relation to model "A"`); + + // fields or references missing + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + a A @relation(fields: [aId]) + aId String + } + `) + ).toContain(`Both "fields" and "references" must be provided`); // one-to-one inconsistent attribute expect( @@ -233,9 +266,41 @@ describe('Data Model Validation Tests', () => { aId String } `) - ).toContain( - `"fields" and "references" must be provided only on one side of relation field` - ); + ).toContain(`"fields" and "references" must be provided only on one side of relation field`); + + // references mismatch + expect( + await loadModelWithError(` + ${prelude} + model A { + myId Int @id + b B? + } + + model B { + id String @id + a A @relation(fields: [aId], references: [id]) + aId String @unique + } + `) + ).toContain(`values of "references" and "fields" must have the same type`); + + // "fields" and "references" typing consistency + expect( + await loadModelWithError(` + ${prelude} + model A { + id Int @id + b B? + } + + model B { + id String @id + a A @relation(fields: [aId], references: [id]) + aId String @unique + } + `) + ).toContain(`values of "references" and "fields" must have the same type`); // one-to-one missing @unique expect( @@ -252,9 +317,7 @@ describe('Data Model Validation Tests', () => { aId String } `) - ).toContain( - `Field "aId" is part of a one-to-one relation and must be marked as @unique` - ); + ).toContain(`Field "aId" is part of a one-to-one relation and must be marked as @unique`); // missing @relation expect( @@ -305,8 +368,6 @@ describe('Data Model Validation Tests', () => { a A @relation(fields: [aId], references: [id]) } `) - ).toContain( - `Could not resolve reference to ReferenceTarget named 'aId'.` - ); + ).toContain(`Could not resolve reference to ReferenceTarget named 'aId'.`); }); }); diff --git a/packages/schema/tests/schema/validation/datasource-validation.test.ts b/packages/schema/tests/schema/validation/datasource-validation.test.ts index 32ff8cf0c..bfd8dea13 100644 --- a/packages/schema/tests/schema/validation/datasource-validation.test.ts +++ b/packages/schema/tests/schema/validation/datasource-validation.test.ts @@ -51,9 +51,7 @@ describe('Datasource Validation Tests', () => { provider = 'abc' } `) - ).toContainEqual( - expect.stringContaining('Provider "abc" is not supported') - ); + ).toContainEqual(expect.stringContaining('Provider "abc" is not supported')); }); it('invalid url value', async () => { @@ -63,9 +61,7 @@ describe('Datasource Validation Tests', () => { url = 123 } `) - ).toContain( - '"url" must be set to a string literal or an invocation of "env" function' - ); + ).toContain('"url" must be set to a string literal or an invocation of "env" function'); expect( await loadModelWithError(` @@ -73,9 +69,7 @@ describe('Datasource Validation Tests', () => { url = uuid() } `) - ).toContain( - '"url" must be set to a string literal or an invocation of "env" function' - ); + ).toContain('"url" must be set to a string literal or an invocation of "env" function'); expect( await loadModelWithError(` @@ -83,9 +77,7 @@ describe('Datasource Validation Tests', () => { shadowDatabaseUrl = 123 } `) - ).toContain( - '"shadowDatabaseUrl" must be set to a string literal or an invocation of "env" function' - ); + ).toContain('"shadowDatabaseUrl" must be set to a string literal or an invocation of "env" function'); }); it('invalid relationMode value', async () => { diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts index ef0776d71..52d8bc731 100644 --- a/packages/schema/tests/schema/validation/schema-validation.test.ts +++ b/packages/schema/tests/schema/validation/schema-validation.test.ts @@ -2,9 +2,7 @@ import { loadModelWithError } from '../../utils'; describe('Toplevel Schema Validation Tests', () => { it('no datasource', async () => { - expect(await loadModelWithError('')).toContain( - 'Model must define a datasource' - ); + expect(await loadModelWithError('')).toContain('Model must define a datasource'); }); it('too many datasources', async () => { diff --git a/packages/schema/tests/utils.ts b/packages/schema/tests/utils.ts index fcd2f5fe5..8f63c091b 100644 --- a/packages/schema/tests/utils.ts +++ b/packages/schema/tests/utils.ts @@ -1,10 +1,10 @@ -import { createZModelServices } from '../src/language-server/zmodel-module'; -import { URI } from 'vscode-uri'; +import { Model } from '@zenstackhq/language/ast'; import * as fs from 'fs'; -import * as path from 'path'; import { NodeFileSystem } from 'langium/node'; -import { Model } from '../src/language-server/generated/ast'; +import * as path from 'path'; import * as tmp from 'tmp'; +import { URI } from 'vscode-uri'; +import { createZModelServices } from '../src/language-server/zmodel-module'; export class SchemaLoadingError extends Error { constructor(public readonly errors: string[]) { @@ -12,35 +12,27 @@ export class SchemaLoadingError extends Error { } } -export async function loadModel( - content: string, - validate = true, - verbose = true -) { +export async function loadModel(content: string, validate = true, verbose = true) { const { name: docPath } = tmp.fileSync({ postfix: '.zmodel' }); fs.writeFileSync(docPath, content); const { shared } = createZModelServices(NodeFileSystem); const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument( URI.file(path.resolve('src/res/stdlib.zmodel')) ); - const doc = shared.workspace.LangiumDocuments.getOrCreateDocument( - URI.file(docPath) - ); + const doc = shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(docPath)); await shared.workspace.DocumentBuilder.build([stdLib, doc], { validationChecks: validate ? 'all' : 'none', }); - const validationErrors = (doc.diagnostics ?? []).filter( - (e) => e.severity === 1 - ); + const validationErrors = (doc.diagnostics ?? []).filter((e) => e.severity === 1); if (validationErrors.length > 0) { for (const validationError of validationErrors) { if (verbose) { const range = doc.textDocument.getText(validationError.range); console.error( - `line ${validationError.range.start.line + 1}: ${ - validationError.message - }${range ? ' [' + range + ']' : ''}` + `line ${validationError.range.start.line + 1}: ${validationError.message}${ + range ? ' [' + range + ']' : '' + }` ); } } diff --git a/packages/schema/tsconfig.json b/packages/schema/tsconfig.json index 835023fea..a4b536651 100644 --- a/packages/schema/tsconfig.json +++ b/packages/schema/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "lib": ["ESNext"], "sourceMap": true, - "outDir": "out", + "outDir": "dist", "strict": true, "noUnusedLocals": true, "noImplicitReturns": true, @@ -13,11 +13,9 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", - "paths": { - "@lang/*": ["src/language-server/*"] - }, - "resolveJsonModule": true + "resolveJsonModule": true, + "declaration": true }, "include": ["src/**/*.ts"], - "exclude": ["out", "node_modules"] + "exclude": ["dist", "bundle", "node_modules", "src/extension.ts"] } diff --git a/packages/sdk/.eslintrc.json b/packages/sdk/.eslintrc.json new file mode 100644 index 000000000..0a913e874 --- /dev/null +++ b/packages/sdk/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/packages/sdk/LICENSE b/packages/sdk/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/packages/sdk/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/sdk/README.md b/packages/sdk/README.md new file mode 100644 index 000000000..4b135ae78 --- /dev/null +++ b/packages/sdk/README.md @@ -0,0 +1 @@ +ZenStack plugin development SDK diff --git a/packages/sdk/package.json b/packages/sdk/package.json new file mode 100644 index 000000000..95cfdc3ae --- /dev/null +++ b/packages/sdk/package.json @@ -0,0 +1,30 @@ +{ + "name": "@zenstackhq/sdk", + "version": "1.0.0-alpha.27", + "description": "ZenStack plugin development SDK", + "main": "index.js", + "scripts": { + "clean": "rimraf dist", + "lint": "eslint src --ext ts", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./LICENSE ./README.md dist", + "watch": "tsc --watch", + "prepublishOnly": "pnpm build", + "publish-dev": "pnpm build && pnpm publish --tag dev" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": true + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@prisma/generator-helper": "^4.7.1", + "@zenstackhq/language": "workspace:*" + }, + "devDependencies": { + "copyfiles": "^2.4.1", + "rimraf": "^3.0.2", + "typescript": "^4.9.4" + } +} diff --git a/packages/sdk/src/ast.ts b/packages/sdk/src/ast.ts new file mode 100644 index 000000000..3407e5b41 --- /dev/null +++ b/packages/sdk/src/ast.ts @@ -0,0 +1 @@ +export * from '@zenstackhq/language/ast'; diff --git a/packages/sdk/src/constants.ts b/packages/sdk/src/constants.ts new file mode 100644 index 000000000..043df9589 --- /dev/null +++ b/packages/sdk/src/constants.ts @@ -0,0 +1,14 @@ +/** + * Auxiliary database field for supporting policy check for nested writes + */ +export const TRANSACTION_FIELD_NAME = 'zenstack_transaction'; + +/** + * Auxiliary database field for building up policy check queries + */ +export const GUARD_FIELD_NAME = 'zenstack_guard'; + +/** + * All Auxiliary fields. + */ +export const AUXILIARY_FIELDS = [TRANSACTION_FIELD_NAME, GUARD_FIELD_NAME]; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts new file mode 100644 index 000000000..5aca99340 --- /dev/null +++ b/packages/sdk/src/index.ts @@ -0,0 +1,3 @@ +export * from './constants'; +export * from './types'; +export * from './utils'; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts new file mode 100644 index 000000000..bf9d2c3c0 --- /dev/null +++ b/packages/sdk/src/types.ts @@ -0,0 +1,30 @@ +import { DMMF } from '@prisma/generator-helper'; +import { Model } from '@zenstackhq/language/ast'; + +/** + * Plugin configuration option value type + */ +export type OptionValue = string | number | boolean; + +/** + * Plugin configuration oiptions + */ +export type PluginOptions = { provider?: string; schemaPath: string } & Record; + +/** + * Plugin entry point function definition + */ +export type PluginFunction = ( + model: Model, + options: PluginOptions, + dmmf?: DMMF.Document +) => Promise | string[] | Promise | void; + +/** + * Plugin error + */ +export class PluginError extends Error { + constructor(message: string) { + super(message); + } +} diff --git a/packages/sdk/src/utils.ts b/packages/sdk/src/utils.ts new file mode 100644 index 000000000..d01f7ffe0 --- /dev/null +++ b/packages/sdk/src/utils.ts @@ -0,0 +1,38 @@ +import { AstNode, Expression, isArrayExpr, isLiteralExpr, Reference } from '@zenstackhq/language/ast'; + +export function resolved(ref: Reference): T { + if (!ref.ref) { + throw new Error(`Reference not resolved: ${ref.$refText}`); + } + return ref.ref; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getLiteral( + expr: Expression | undefined +): T | undefined { + if (!isLiteralExpr(expr)) { + return undefined; + } + return expr.value as T; +} + +export function getArray(expr: Expression | undefined): Expression[] | undefined { + return isArrayExpr(expr) ? expr.items : undefined; +} + +export function getLiteralArray< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends string | number | boolean | any = any +>(expr: Expression | undefined): (T | undefined)[] | undefined { + const arr = getArray(expr); + if (!arr) { + return undefined; + } + return arr.map((item) => getLiteral(item)); +} + +export default function indentString(string: string, count = 4): string { + const indent = ' '; + return string.replace(/^(?!\s*$)/gm, indent.repeat(count)); +} diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json new file mode 100644 index 000000000..f6dff1942 --- /dev/null +++ b/packages/sdk/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "CommonJS", + "lib": ["ESNext", "DOM"], + "sourceMap": true, + "outDir": "dist", + "strict": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "resolveJsonModule": true, + "strictPropertyInitialization": false, + "paths": {} + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df222095e..628bf427f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,37 +4,119 @@ importers: .: specifiers: - '@changesets/cli': ^2.25.2 + '@changesets/cli': ^2.26.0 devDependencies: - '@changesets/cli': 2.25.2 + '@changesets/cli': 2.26.0 - packages/next-auth: + packages/language: specifiers: - '@next-auth/prisma-adapter': ^1.0.5 - '@types/bcryptjs': ^2.4.2 + concurrently: ^7.4.0 + copyfiles: ^2.4.1 + langium: ^1.0.1 + langium-cli: ^1.0.0 + rimraf: ^3.0.2 + typescript: ^4.9.4 + dependencies: + langium: 1.0.1 + devDependencies: + concurrently: 7.4.0 + copyfiles: 2.4.1 + langium-cli: 1.0.0 + rimraf: 3.0.2 + typescript: 4.9.4 + publishDirectory: dist + + packages/next: + specifiers: + '@types/react': ^18.0.26 '@zenstackhq/runtime': workspace:* - bcryptjs: ^2.4.3 - next-auth: ^4.0.0 + copyfiles: ^2.4.1 + next: ^12.3.1 || ^13 + react: ^17.0.2 || ^18 rimraf: ^3.0.2 - typescript: ^4.9.3 + superjson: ^1.11.0 + typescript: ^4.9.4 dependencies: - '@next-auth/prisma-adapter': 1.0.5_w3qkztkwfy6eylhe7n5itugygy '@zenstackhq/runtime': link:../runtime/dist - bcryptjs: 2.4.3 + next: 12.3.1_biqbaboplfbrettd7655fr4n2y + superjson: 1.11.0 devDependencies: - '@types/bcryptjs': 2.4.2 - next-auth: 4.17.0_azq6kxkn3od7qdylwkyksrwopy + '@types/react': 18.0.26 + copyfiles: 2.4.1 + react: 18.2.0 rimraf: 3.0.2 - typescript: 4.9.3 + typescript: 4.9.4 + publishDirectory: dist + + packages/plugins/react: + specifiers: + '@prisma/generator-helper': ^4.7.1 + '@types/react': ^18.0.26 + '@zenstackhq/sdk': workspace:* + change-case: ^4.1.2 + copyfiles: ^2.4.1 + decimal.js: ^10.4.2 + rimraf: ^3.0.2 + superjson: ^1.11.0 + swr: ^1.3.0 + ts-morph: ^16.0.0 + typescript: ^4.9.4 + dependencies: + '@prisma/generator-helper': 4.7.1 + '@zenstackhq/sdk': link:../../sdk/dist + change-case: 4.1.2 + decimal.js: 10.4.2 + superjson: 1.12.1 + swr: 1.3.0_react@18.2.0 + ts-morph: 16.0.0 + devDependencies: + '@types/react': 18.0.26 + copyfiles: 2.4.1 + rimraf: 3.0.2 + typescript: 4.9.4 + publishDirectory: dist + + packages/plugins/trpc: + specifiers: + '@prisma/generator-helper': ^4.7.1 + '@prisma/internals': ^4.7.1 + '@types/prettier': ^2.7.2 + '@zenstackhq/sdk': workspace:* + change-case: ^4.1.2 + copyfiles: ^2.4.1 + prettier: ^2.8.3 + rimraf: ^3.0.2 + ts-morph: ^16.0.0 + tslib: ^2.4.1 + typescript: ^4.9.4 + zod: ^3.19.1 + dependencies: + '@prisma/generator-helper': 4.7.1 + '@prisma/internals': 4.7.1 + '@zenstackhq/sdk': link:../../sdk/dist + change-case: 4.1.2 + prettier: 2.8.3 + ts-morph: 16.0.0 + tslib: 2.4.1 + zod: 3.19.1 + devDependencies: + '@types/prettier': 2.7.2 + copyfiles: 2.4.1 + rimraf: 3.0.2 + typescript: 4.9.4 publishDirectory: dist packages/runtime: specifiers: + '@prisma/client': ^4.0.0 '@types/bcryptjs': ^2.4.2 '@types/jest': ^29.0.3 '@types/node': ^14.18.29 + '@zenstackhq/sdk': workspace:* bcryptjs: ^2.4.3 + change-case: ^4.1.2 colors: 1.4.0 + copyfiles: ^2.4.1 cuid: ^2.1.8 decimal.js: ^10.4.2 deepcopy: ^2.1.0 @@ -49,8 +131,11 @@ importers: zod: ^3.19.1 zod-validation-error: ^0.2.1 dependencies: + '@prisma/client': 4.7.1 '@types/bcryptjs': 2.4.2 + '@zenstackhq/sdk': link:../sdk/dist bcryptjs: 2.4.3 + change-case: 4.1.2 colors: 1.4.0 cuid: 2.1.8 decimal.js: 10.4.2 @@ -66,50 +151,58 @@ importers: devDependencies: '@types/jest': 29.2.0 '@types/node': 14.18.32 + copyfiles: 2.4.1 rimraf: 3.0.2 typescript: 4.9.3 publishDirectory: dist packages/schema: specifiers: - '@prisma/internals': ~4.7.0 + '@prisma/generator-helper': ^4.7.1 + '@prisma/internals': ^4.7.1 '@types/async-exit-hook': ^2.0.0 '@types/jest': ^29.2.0 '@types/node': ^14.18.32 '@types/pluralize': ^0.0.29 + '@types/semver': ^7.3.13 '@types/tmp': ^0.2.3 '@types/uuid': ^8.3.4 '@types/vscode': ^1.56.0 '@typescript-eslint/eslint-plugin': ^5.42.0 '@typescript-eslint/parser': ^5.42.0 - '@zenstackhq/runtime': workspace:../runtime/dist + '@zenstackhq/language': workspace:* + '@zenstackhq/runtime': workspace:* + '@zenstackhq/sdk': workspace:* async-exit-hook: ^2.0.1 change-case: ^4.1.2 chevrotain: ^9.1.0 colors: 1.4.0 commander: ^8.3.0 concurrently: ^7.4.0 + copyfiles: ^2.4.1 cuid: ^2.1.8 dotenv: ^16.0.3 esbuild: ^0.15.12 eslint: ^8.27.0 + eslint-plugin-jest: ^27.1.7 jest: ^29.2.1 - langium: ^0.5.0 - langium-cli: ^0.5.0 + langium: ^1.0.1 + langium-cli: ^1.0.0 mixpanel: ^0.17.0 node-machine-id: ^1.1.12 - ora: ^6.1.2 + ora: ^5.4.1 pluralize: ^8.0.0 prisma: ~4.7.0 promisify: ^0.0.3 + renamer: ^4.0.0 rimraf: ^3.0.2 + semver: ^7.3.8 sleep-promise: ^9.1.0 tmp: ^0.2.1 ts-jest: ^29.0.3 ts-morph: ^16.0.0 ts-node: ^10.9.1 tsc-alias: ^1.7.0 - tsconfig-paths-jest: ^0.0.1 typescript: ^4.8.4 uuid: ^9.0.0 vsce: ^2.13.0 @@ -118,21 +211,27 @@ importers: vscode-languageserver: ^8.0.2 vscode-languageserver-textdocument: ^1.0.7 vscode-uri: ^3.0.6 + zod: ^3.19.1 dependencies: + '@prisma/generator-helper': 4.7.1 + '@prisma/internals': 4.7.1 + '@zenstackhq/language': link:../language/dist '@zenstackhq/runtime': link:../runtime/dist + '@zenstackhq/sdk': link:../sdk/dist async-exit-hook: 2.0.1 change-case: 4.1.2 chevrotain: 9.1.0 colors: 1.4.0 commander: 8.3.0 cuid: 2.1.8 - langium: 0.5.0 + langium: 1.0.1 mixpanel: 0.17.0 node-machine-id: 1.1.12 - ora: 6.1.2 + ora: 5.4.1 pluralize: 8.0.0 prisma: 4.7.0 promisify: 0.0.3 + semver: 7.3.8 sleep-promise: 9.1.0 ts-morph: 16.0.0 uuid: 9.0.0 @@ -141,52 +240,85 @@ importers: vscode-languageserver: 8.0.2 vscode-languageserver-textdocument: 1.0.7 vscode-uri: 3.0.6 + zod: 3.19.1 devDependencies: - '@prisma/internals': 4.7.0 '@types/async-exit-hook': 2.0.0 '@types/jest': 29.2.0 '@types/node': 14.18.32 '@types/pluralize': 0.0.29 + '@types/semver': 7.3.13 '@types/tmp': 0.2.3 '@types/uuid': 8.3.4 '@types/vscode': 1.72.0 '@typescript-eslint/eslint-plugin': 5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34 '@typescript-eslint/parser': 5.42.0_rmayb2veg2btbq6mbmnyivgasy concurrently: 7.4.0 + copyfiles: 2.4.1 dotenv: 16.0.3 esbuild: 0.15.12 eslint: 8.27.0 + eslint-plugin-jest: 27.1.7_pdoqwsgoaseomjcjdinsmwbl4q jest: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze - langium-cli: 0.5.0 + langium-cli: 1.0.0 + renamer: 4.0.0 rimraf: 3.0.2 tmp: 0.2.1 - ts-jest: 29.0.3_nvckv3qbfhmmsla6emqlkyje4a + ts-jest: 29.0.3_yf6yylffq7wy4s6j6no7bitnpa ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme tsc-alias: 1.7.0 - tsconfig-paths-jest: 0.0.1 typescript: 4.8.4 vsce: 2.13.0 + publishDirectory: dist + + packages/sdk: + specifiers: + '@prisma/generator-helper': ^4.7.1 + '@zenstackhq/language': workspace:* + copyfiles: ^2.4.1 + rimraf: ^3.0.2 + typescript: ^4.9.4 + dependencies: + '@prisma/generator-helper': 4.7.1 + '@zenstackhq/language': link:../language/dist + devDependencies: + copyfiles: 2.4.1 + rimraf: 3.0.2 + typescript: 4.9.4 + publishDirectory: dist tests/integration: specifiers: + '@prisma/client': ^4.7.0 '@types/bcryptjs': ^2.4.2 + '@types/fs-extra': ^11.0.1 '@types/jest': ^29.0.3 '@types/node': ^14.18.29 '@types/supertest': ^2.0.12 '@types/tmp': ^0.2.3 + '@types/uuid': ^8.3.4 + '@zenstackhq/next': workspace:* + '@zenstackhq/react': workspace:* + '@zenstackhq/runtime': workspace:* + '@zenstackhq/trpc': workspace:* bcryptjs: ^2.4.3 decimal.js: ^10.4.2 + eslint: ^8.30.0 + eslint-plugin-jest: ^27.1.7 + fs-extra: ^11.1.0 jest: ^29.0.3 jest-fetch-mock: ^3.0.3 next: ^12.3.1 + prisma: ~4.7.0 sleep-promise: ^9.1.0 superjson: ^1.11.0 - supertest: ^6.3.0 tmp: ^0.2.1 ts-jest: ^29.0.1 ts-node: ^10.9.1 typescript: ^4.6.2 + uuid: ^9.0.0 + zenstack: 'workspace: *' dependencies: + '@prisma/client': 4.7.1_prisma@4.7.0 '@types/node': 14.18.29 bcryptjs: 2.4.3 decimal.js: 10.4.2 @@ -194,17 +326,28 @@ importers: superjson: 1.11.0 devDependencies: '@types/bcryptjs': 2.4.2 + '@types/fs-extra': 11.0.1 '@types/jest': 29.0.3 '@types/supertest': 2.0.12 '@types/tmp': 0.2.3 + '@types/uuid': 8.3.4 + '@zenstackhq/next': link:../../packages/next/dist + '@zenstackhq/react': link:../../packages/plugins/react/dist + '@zenstackhq/runtime': link:../../packages/runtime/dist + '@zenstackhq/trpc': link:../../packages/plugins/trpc/dist + eslint: 8.30.0 + eslint-plugin-jest: 27.1.7_mqrelppxlcv42tihlyzz2qch5q + fs-extra: 11.1.0 jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 jest-fetch-mock: 3.0.3 - next: 12.3.1_6tziyx3dehkoeijunclpkpolha - supertest: 6.3.0 + next: 12.3.1_672uxklweod7ene3nqtsh262ca + prisma: 4.7.0 tmp: 0.2.1 - ts-jest: 29.0.1_poggjixajg6vd6yquly7s7dsj4 + ts-jest: 29.0.1_57hartw4ijnqan4zaqtggk6uaa ts-node: 10.9.1_ck2axrxkiif44rdbzjywaqjysa typescript: 4.8.3 + uuid: 9.0.0 + zenstack: link:../../packages/schema/dist packages: @@ -221,13 +364,17 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 - dev: true /@babel/compat-data/7.19.4: resolution: {integrity: sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==} engines: {node: '>=6.9.0'} dev: true + /@babel/compat-data/7.20.5: + resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/core/7.19.3: resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==} engines: {node: '>=6.9.0'} @@ -251,24 +398,24 @@ packages: - supports-color dev: true - /@babel/core/7.19.6: - resolution: {integrity: sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==} + /@babel/core/7.20.5: + resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 - '@babel/generator': 7.19.6 - '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.6 - '@babel/helper-module-transforms': 7.19.6 - '@babel/helpers': 7.19.4 - '@babel/parser': 7.19.6 + '@babel/generator': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helpers': 7.20.6 + '@babel/parser': 7.20.5 '@babel/template': 7.18.10 - '@babel/traverse': 7.19.6 - '@babel/types': 7.19.4 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 - json5: 2.2.1 + json5: 2.2.3 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -292,6 +439,15 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator/7.20.5: + resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.5 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.3: resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} engines: {node: '>=6.9.0'} @@ -305,14 +461,14 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.6: - resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: + resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.19.4 - '@babel/core': 7.19.6 + '@babel/compat-data': 7.20.5 + '@babel/core': 7.20.5 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 semver: 6.3.0 @@ -328,21 +484,21 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@babel/helper-hoist-variables/7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@babel/helper-module-imports/7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@babel/helper-module-transforms/7.19.0: @@ -361,18 +517,18 @@ packages: - supports-color dev: true - /@babel/helper-module-transforms/7.19.6: - resolution: {integrity: sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==} + /@babel/helper-module-transforms/7.20.2: + resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.19.4 + '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.18.10 - '@babel/traverse': 7.19.6 - '@babel/types': 7.19.4 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 transitivePeerDependencies: - supports-color dev: true @@ -382,6 +538,11 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-plugin-utils/7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-simple-access/7.18.6: resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} engines: {node: '>=6.9.0'} @@ -389,18 +550,18 @@ packages: '@babel/types': 7.19.4 dev: true - /@babel/helper-simple-access/7.19.4: - resolution: {integrity: sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==} + /@babel/helper-simple-access/7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@babel/helper-split-export-declaration/7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@babel/helper-string-parser/7.19.4: @@ -411,7 +572,6 @@ packages: /@babel/helper-validator-identifier/7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option/7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} @@ -429,6 +589,17 @@ packages: - supports-color dev: true + /@babel/helpers/7.20.6: + resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/highlight/7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} @@ -436,7 +607,6 @@ packages: '@babel/helper-validator-identifier': 7.19.1 chalk: 2.4.2 js-tokens: 4.0.0 - dev: true /@babel/parser/7.19.4: resolution: {integrity: sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==} @@ -454,22 +624,30 @@ packages: '@babel/types': 7.19.4 dev: true + /@babel/parser/7.20.5: + resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.20.5 + dev: true + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.19.3: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.19.6: + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.5: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.19.3: @@ -481,12 +659,12 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.19.0 dev: true @@ -496,16 +674,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.19.6: + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.5: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.3: @@ -517,12 +695,12 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.6: + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.5: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.19.0 dev: true @@ -532,16 +710,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.3: @@ -554,13 +732,13 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.6: + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.5: resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.19.0 dev: true @@ -570,16 +748,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.19.6: + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.5: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.19.3: @@ -588,16 +766,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.19.3: @@ -606,16 +784,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.19.6: + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.5: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.19.3: @@ -624,16 +802,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.19.3: @@ -642,16 +820,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.19.3: @@ -660,16 +838,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.19.6: + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.19.3: @@ -679,17 +857,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.19.3 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.19.6: + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.5: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.19.3: @@ -702,29 +880,40 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.19.6: + /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.20.5: resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/runtime/7.20.1: - resolution: {integrity: sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==} + /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.5: + resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/runtime/7.20.7: + resolution: {integrity: sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 + dev: true /@babel/template/7.18.10: resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.19.6 - '@babel/types': 7.19.4 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 dev: true /@babel/traverse/7.19.4: @@ -763,6 +952,24 @@ packages: - supports-color dev: true + /@babel/traverse/7.20.5: + resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.5 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types/7.19.4: resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==} engines: {node: '>=6.9.0'} @@ -772,63 +979,72 @@ packages: to-fast-properties: 2.0.0 dev: true + /@babel/types/7.20.5: + resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + /@bcoe/v8-coverage/0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@changesets/apply-release-plan/6.1.2: - resolution: {integrity: sha512-H8TV9E/WtJsDfoDVbrDGPXmkZFSv7W2KLqp4xX4MKZXshb0hsQZUNowUa8pnus9qb/5OZrFFRVsUsDCVHNW/AQ==} + /@changesets/apply-release-plan/6.1.3: + resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: - '@babel/runtime': 7.20.1 - '@changesets/config': 2.2.0 + '@babel/runtime': 7.20.7 + '@changesets/config': 2.3.0 '@changesets/get-version-range-type': 0.3.2 - '@changesets/git': 1.5.0 - '@changesets/types': 5.2.0 + '@changesets/git': 2.0.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 lodash.startcase: 4.4.0 outdent: 0.5.0 - prettier: 2.8.0 + prettier: 2.8.3 resolve-from: 5.0.0 semver: 5.7.1 dev: true - /@changesets/assemble-release-plan/5.2.2: - resolution: {integrity: sha512-B1qxErQd85AeZgZFZw2bDKyOfdXHhG+X5S+W3Da2yCem8l/pRy4G/S7iOpEcMwg6lH8q2ZhgbZZwZ817D+aLuQ==} + /@changesets/assemble-release-plan/5.2.3: + resolution: {integrity: sha512-g7EVZCmnWz3zMBAdrcKhid4hkHT+Ft1n0mLussFMcB1dE2zCuwcvGoy9ec3yOgPGF4hoMtgHaMIk3T3TBdvU9g==} dependencies: - '@babel/runtime': 7.20.1 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/types': 5.2.0 + '@changesets/get-dependents-graph': 1.3.5 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 semver: 5.7.1 dev: true - /@changesets/changelog-git/0.1.13: - resolution: {integrity: sha512-zvJ50Q+EUALzeawAxax6nF2WIcSsC5PwbuLeWkckS8ulWnuPYx8Fn/Sjd3rF46OzeKA8t30loYYV6TIzp4DIdg==} + /@changesets/changelog-git/0.1.14: + resolution: {integrity: sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 dev: true - /@changesets/cli/2.25.2: - resolution: {integrity: sha512-ACScBJXI3kRyMd2R8n8SzfttDHi4tmKSwVwXBazJOylQItSRSF4cGmej2E4FVf/eNfGy6THkL9GzAahU9ErZrA==} + /@changesets/cli/2.26.0: + resolution: {integrity: sha512-0cbTiDms+ICTVtEwAFLNW0jBNex9f5+fFv3I771nBvdnV/mOjd1QJ4+f8KtVSOrwD9SJkk9xbDkWFb0oXd8d1Q==} hasBin: true dependencies: - '@babel/runtime': 7.20.1 - '@changesets/apply-release-plan': 6.1.2 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/changelog-git': 0.1.13 - '@changesets/config': 2.2.0 + '@babel/runtime': 7.20.7 + '@changesets/apply-release-plan': 6.1.3 + '@changesets/assemble-release-plan': 5.2.3 + '@changesets/changelog-git': 0.1.14 + '@changesets/config': 2.3.0 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/get-release-plan': 3.0.15 - '@changesets/git': 1.5.0 + '@changesets/get-dependents-graph': 1.3.5 + '@changesets/get-release-plan': 3.0.16 + '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 - '@changesets/write': 0.2.2 + '@changesets/pre': 1.0.14 + '@changesets/read': 0.5.9 + '@changesets/types': 5.2.1 + '@changesets/write': 0.2.3 '@manypkg/get-packages': 1.1.3 '@types/is-ci': 3.0.0 '@types/semver': 6.2.3 @@ -850,13 +1066,13 @@ packages: tty-table: 4.1.6 dev: true - /@changesets/config/2.2.0: - resolution: {integrity: sha512-GGaokp3nm5FEDk/Fv2PCRcQCOxGKKPRZ7prcMqxEr7VSsG75MnChQE8plaW1k6V8L2bJE+jZWiRm19LbnproOw==} + /@changesets/config/2.3.0: + resolution: {integrity: sha512-EgP/px6mhCx8QeaMAvWtRrgyxW08k/Bx2tpGT+M84jEdX37v3VKfh4Cz1BkwrYKuMV2HZKeHOh8sHvja/HcXfQ==} dependencies: '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 + '@changesets/get-dependents-graph': 1.3.5 '@changesets/logger': 0.0.5 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.5 @@ -868,25 +1084,25 @@ packages: extendable-error: 0.1.7 dev: true - /@changesets/get-dependents-graph/1.3.4: - resolution: {integrity: sha512-+C4AOrrFY146ydrgKOo5vTZfj7vetNu1tWshOID+UjPUU9afYGDXI8yLnAeib1ffeBXV3TuGVcyphKpJ3cKe+A==} + /@changesets/get-dependents-graph/1.3.5: + resolution: {integrity: sha512-w1eEvnWlbVDIY8mWXqWuYE9oKhvIaBhzqzo4ITSJY9hgoqQ3RoBqwlcAzg11qHxv/b8ReDWnMrpjpKrW6m1ZTA==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 semver: 5.7.1 dev: true - /@changesets/get-release-plan/3.0.15: - resolution: {integrity: sha512-W1tFwxE178/en+zSj/Nqbc3mvz88mcdqUMJhRzN1jDYqN3QI4ifVaRF9mcWUU+KI0gyYEtYR65tour690PqTcA==} + /@changesets/get-release-plan/3.0.16: + resolution: {integrity: sha512-OpP9QILpBp1bY2YNIKFzwigKh7Qe9KizRsZomzLe6pK8IUo8onkAAVUD8+JRKSr8R7d4+JRuQrfSSNlEwKyPYg==} dependencies: - '@babel/runtime': 7.20.1 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/config': 2.2.0 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 + '@babel/runtime': 7.20.7 + '@changesets/assemble-release-plan': 5.2.3 + '@changesets/config': 2.3.0 + '@changesets/pre': 1.0.14 + '@changesets/read': 0.5.9 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 dev: true @@ -894,14 +1110,15 @@ packages: resolution: {integrity: sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==} dev: true - /@changesets/git/1.5.0: - resolution: {integrity: sha512-Xo8AT2G7rQJSwV87c8PwMm6BAc98BnufRMsML7m7Iw8Or18WFvFmxqG5aOL5PBvhgq9KrKvaeIBNIymracSuHg==} + /@changesets/git/2.0.0: + resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} dependencies: - '@babel/runtime': 7.20.1 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 + micromatch: 4.0.5 spawndamnit: 2.0.0 dev: true @@ -911,31 +1128,31 @@ packages: chalk: 2.4.2 dev: true - /@changesets/parse/0.3.15: - resolution: {integrity: sha512-3eDVqVuBtp63i+BxEWHPFj2P1s3syk0PTrk2d94W9JD30iG+OER0Y6n65TeLlY8T2yB9Fvj6Ev5Gg0+cKe/ZUA==} + /@changesets/parse/0.3.16: + resolution: {integrity: sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 js-yaml: 3.14.1 dev: true - /@changesets/pre/1.0.13: - resolution: {integrity: sha512-jrZc766+kGZHDukjKhpBXhBJjVQMied4Fu076y9guY1D3H622NOw8AQaLV3oQsDtKBTrT2AUFjt9Z2Y9Qx+GfA==} + /@changesets/pre/1.0.14: + resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} dependencies: - '@babel/runtime': 7.20.1 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 dev: true - /@changesets/read/0.5.8: - resolution: {integrity: sha512-eYaNfxemgX7f7ELC58e7yqQICW5FB7V+bd1lKt7g57mxUrTveYME+JPaBPpYx02nP53XI6CQp6YxnR9NfmFPKw==} + /@changesets/read/0.5.9: + resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} dependencies: - '@babel/runtime': 7.20.1 - '@changesets/git': 1.5.0 + '@babel/runtime': 7.20.7 + '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 - '@changesets/parse': 0.3.15 - '@changesets/types': 5.2.0 + '@changesets/parse': 0.3.16 + '@changesets/types': 5.2.1 chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -945,25 +1162,46 @@ packages: resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} dev: true - /@changesets/types/5.2.0: - resolution: {integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==} + /@changesets/types/5.2.1: + resolution: {integrity: sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==} dev: true - /@changesets/write/0.2.2: - resolution: {integrity: sha512-kCYNHyF3xaId1Q/QE+DF3UTrHTyg3Cj/f++T8S8/EkC+jh1uK2LFnM9h+EzV+fsmnZDrs7r0J4LLpeI/VWC5Hg==} + /@changesets/write/0.2.3: + resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} dependencies: - '@babel/runtime': 7.20.1 - '@changesets/types': 5.2.0 + '@babel/runtime': 7.20.7 + '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 - prettier: 2.8.0 + prettier: 2.8.3 dev: true + /@chevrotain/cst-dts-gen/10.4.2: + resolution: {integrity: sha512-0+4bNjlndNWMoVLH/+y4uHnf6GrTipsC+YTppJxelVJo+xeRVQ0s2PpkdDCVTsu7efyj+8r1gFiwVXsp6JZ0iQ==} + dependencies: + '@chevrotain/gast': 10.4.2 + '@chevrotain/types': 10.4.2 + lodash: 4.17.21 + + /@chevrotain/gast/10.4.2: + resolution: {integrity: sha512-4ZAn8/mjkmYonilSJ60gGj1tAF0cVWYUMlIGA0e4ATAc3a648aCnvpBw7zlPHDQjFp50XC13iyWEgWAKiRKTOA==} + dependencies: + '@chevrotain/types': 10.4.2 + lodash: 4.17.21 + + /@chevrotain/types/10.4.2: + resolution: {integrity: sha512-QzSCjg6G4MvIoLeIgOiMR0IgzkGEQqrNJJIr3T5ETRa7l4Av4AMIiEctV99mvDr57iXwwk0/kr3RJxiU36Nevw==} + /@chevrotain/types/9.1.0: resolution: {integrity: sha512-3hbCD1CThkv9gnaSIPq0GUXwKni68e0ph6jIHwCvcWiQ4JB2xi8bFxBain0RF04qHUWuDjgnZLj4rLgimuGO+g==} + dev: false + + /@chevrotain/utils/10.4.2: + resolution: {integrity: sha512-V34dacxWLwKcvcy32dx96ADJVdB7kOJLm7LyBkBQw5u5HC9WdEFw2G17zml+U3ivavGTrGPJHl8o9/UJm0PlUw==} /@chevrotain/utils/9.1.0: resolution: {integrity: sha512-llLJZ8OAlZrjGlBvamm6Zdo/HmGAcCLq5gx7cSwUX8No+n/8ip+oaC4x33IdZIif8+Rh5dQUIZXmfbSghiOmNQ==} + dev: false /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -1007,6 +1245,23 @@ packages: - supports-color dev: true + /@eslint/eslintrc/1.4.0: + resolution: {integrity: sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.4.1 + globals: 13.19.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/config-array/0.11.7: resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==} engines: {node: '>=10.10.0'} @@ -1018,6 +1273,17 @@ packages: - supports-color dev: true + /@humanwhocodes/config-array/0.11.8: + resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/module-importer/1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} @@ -1048,7 +1314,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 jest-message-util: 29.0.3 jest-util: 29.0.3 @@ -1060,7 +1326,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 jest-message-util: 29.2.1 jest-util: 29.2.1 @@ -1081,14 +1347,14 @@ packages: '@jest/test-result': 29.0.3 '@jest/transform': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.4.0 exit: 0.1.2 graceful-fs: 4.2.10 jest-changed-files: 29.0.0 - jest-config: 29.0.3_uo4il2aklsrxuk4ro37qmnu2ge + jest-config: 29.0.3_sfpnjnnyjha3fwltwg26xa3eti jest-haste-map: 29.0.3 jest-message-util: 29.0.3 jest-regex-util: 29.0.0 @@ -1123,14 +1389,14 @@ packages: '@jest/test-result': 29.2.1 '@jest/transform': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.5.0 exit: 0.1.2 graceful-fs: 4.2.10 jest-changed-files: 29.2.0 - jest-config: 29.2.1_uo4il2aklsrxuk4ro37qmnu2ge + jest-config: 29.2.1_sfpnjnnyjha3fwltwg26xa3eti jest-haste-map: 29.2.1 jest-message-util: 29.2.1 jest-regex-util: 29.2.0 @@ -1157,7 +1423,7 @@ packages: dependencies: '@jest/fake-timers': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-mock: 29.0.3 dev: true @@ -1167,10 +1433,20 @@ packages: dependencies: '@jest/fake-timers': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-mock: 29.2.1 dev: true + /@jest/environment/29.3.1: + resolution: {integrity: sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.3.1 + '@jest/types': 29.3.1 + '@types/node': 18.11.18 + jest-mock: 29.3.1 + dev: true + /@jest/expect-utils/29.0.3: resolution: {integrity: sha512-i1xUkau7K/63MpdwiRqaxgZOjxYs4f0WMTGJnYwUKubsNRZSeQbLorS7+I4uXVF9KQ5r61BUPAUMZ7Lf66l64Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1185,6 +1461,13 @@ packages: jest-get-type: 29.2.0 dev: true + /@jest/expect-utils/29.3.1: + resolution: {integrity: sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.2.0 + dev: true + /@jest/expect/29.0.3: resolution: {integrity: sha512-6W7K+fsI23FQ01H/BWccPyDZFrnU9QlzDcKOjrNVU5L8yUORFAJJIpmyxWPW70+X624KUNqzZwPThPMX28aXEQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1205,13 +1488,23 @@ packages: - supports-color dev: true + /@jest/expect/29.3.1: + resolution: {integrity: sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.3.1 + jest-snapshot: 29.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers/29.0.3: resolution: {integrity: sha512-tmbUIo03x0TdtcZCESQ0oQSakPCpo7+s6+9mU19dd71MptkP4zCwoeZqna23//pgbhtT1Wq02VmA9Z9cNtvtCQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.3 '@sinonjs/fake-timers': 9.1.2 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-message-util: 29.0.3 jest-mock: 29.0.3 jest-util: 29.0.3 @@ -1223,22 +1516,22 @@ packages: dependencies: '@jest/types': 29.2.1 '@sinonjs/fake-timers': 9.1.2 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-message-util: 29.2.1 jest-mock: 29.2.1 jest-util: 29.2.1 dev: true - /@jest/globals/29.0.3: - resolution: {integrity: sha512-YqGHT65rFY2siPIHHFjuCGUsbzRjdqkwbat+Of6DmYRg5shIXXrLdZoVE/+TJ9O1dsKsFmYhU58JvIbZRU1Z9w==} + /@jest/fake-timers/29.3.1: + resolution: {integrity: sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.0.3 - '@jest/expect': 29.0.3 - '@jest/types': 29.0.3 - jest-mock: 29.0.3 - transitivePeerDependencies: - - supports-color + '@jest/types': 29.3.1 + '@sinonjs/fake-timers': 9.1.2 + '@types/node': 18.11.18 + jest-message-util: 29.3.1 + jest-mock: 29.3.1 + jest-util: 29.3.1 dev: true /@jest/globals/29.2.1: @@ -1253,6 +1546,18 @@ packages: - supports-color dev: true + /@jest/globals/29.3.1: + resolution: {integrity: sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.3.1 + '@jest/expect': 29.3.1 + '@jest/types': 29.3.1 + jest-mock: 29.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/reporters/29.0.3: resolution: {integrity: sha512-3+QU3d4aiyOWfmk1obDerie4XNCaD5Xo1IlKNde2yGEi02WQD+ZQD0i5Hgqm1e73sMV7kw6pMlCnprtEwEVwxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1268,7 +1573,7 @@ packages: '@jest/transform': 29.0.3 '@jest/types': 29.0.3 '@jridgewell/trace-mapping': 0.3.16 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1306,7 +1611,7 @@ packages: '@jest/transform': 29.2.1 '@jest/types': 29.2.1 '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1420,7 +1725,7 @@ packages: resolution: {integrity: sha512-xup+iEuaIRSQabQaeqxaQyN0vg1Dctrp9oTObQsNf3sZEowTIa5cANYuoyi8Tqhg4GCqEVLTf18KW7ii0UeFVA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@jest/types': 29.2.1 '@jridgewell/trace-mapping': 0.3.17 babel-plugin-istanbul: 6.1.1 @@ -1439,6 +1744,29 @@ packages: - supports-color dev: true + /@jest/transform/29.3.1: + resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.20.5 + '@jest/types': 29.3.1 + '@jridgewell/trace-mapping': 0.3.17 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.10 + jest-haste-map: 29.3.1 + jest-regex-util: 29.2.0 + jest-util: 29.3.1 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types/29.0.3: resolution: {integrity: sha512-coBJmOQvurXjN1Hh5PzF7cmsod0zLIOXpP8KD161mqNlroMhLcwpODiEzi7ZsRl5Z/AIuxpeNm8DCl43F4kz8A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1446,7 +1774,7 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 '@types/yargs': 17.0.12 chalk: 4.1.2 dev: true @@ -1458,7 +1786,19 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 + '@types/yargs': 17.0.13 + chalk: 4.1.2 + dev: true + + /@jest/types/29.3.1: + resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.11.18 '@types/yargs': 17.0.13 chalk: 4.1.2 dev: true @@ -1518,7 +1858,7 @@ packages: /@manypkg/find-root/1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.20.1 + '@babel/runtime': 7.20.7 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -1527,7 +1867,7 @@ packages: /@manypkg/get-packages/1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.20.1 + '@babel/runtime': 7.20.7 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -1535,16 +1875,6 @@ packages: read-yaml-file: 1.1.0 dev: true - /@next-auth/prisma-adapter/1.0.5_w3qkztkwfy6eylhe7n5itugygy: - resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==} - peerDependencies: - '@prisma/client': '>=2.26.0 || >=3' - next-auth: ^4 - dependencies: - '@prisma/client': 4.7.1 - next-auth: 4.17.0_azq6kxkn3od7qdylwkyksrwopy - dev: false - /@next/env/12.3.1: resolution: {integrity: sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==} @@ -1668,53 +1998,50 @@ packages: engines: {node: '>= 8'} dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.13.0 + fastq: 1.15.0 - /@opentelemetry/api/1.2.0: - resolution: {integrity: sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==} + /@opentelemetry/api/1.3.0: + resolution: {integrity: sha512-YveTnGNsFFixTKJz09Oi4zYkiLT5af3WpZDu4aIUM7xX+2bHAkOJayFTVQd6zB8kkWPpbua4Ha6Ql00grdLlJQ==} engines: {node: '>=8.0.0'} - dev: true + dev: false - /@opentelemetry/core/1.7.0_@opentelemetry+api@1.2.0: - resolution: {integrity: sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ==} + /@opentelemetry/core/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.3.0' + '@opentelemetry/api': '>=1.0.0 <1.4.0' dependencies: - '@opentelemetry/api': 1.2.0 - '@opentelemetry/semantic-conventions': 1.7.0 - dev: true + '@opentelemetry/api': 1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false - /@opentelemetry/resources/1.7.0_@opentelemetry+api@1.2.0: - resolution: {integrity: sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg==} + /@opentelemetry/resources/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-KSyMH6Jvss/PFDy16z5qkCK0ERlpyqixb1xwb73wLMvVq+j7i89lobDjw3JkpCcd1Ws0J6jAI4fw28Zufj2ssg==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.3.0' + '@opentelemetry/api': '>=1.0.0 <1.4.0' dependencies: - '@opentelemetry/api': 1.2.0 - '@opentelemetry/core': 1.7.0_@opentelemetry+api@1.2.0 - '@opentelemetry/semantic-conventions': 1.7.0 - dev: true + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false - /@opentelemetry/sdk-trace-base/1.7.0_@opentelemetry+api@1.2.0: - resolution: {integrity: sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg==} + /@opentelemetry/sdk-trace-base/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-iH41m0UTddnCKJzZx3M85vlhKzRcmT48pUeBbnzsGrq4nIay1oWVHKM5nhB5r8qRDGvd/n7f/YLCXClxwM0tvA==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.3.0' + '@opentelemetry/api': '>=1.0.0 <1.4.0' dependencies: - '@opentelemetry/api': 1.2.0 - '@opentelemetry/core': 1.7.0_@opentelemetry+api@1.2.0 - '@opentelemetry/resources': 1.7.0_@opentelemetry+api@1.2.0 - '@opentelemetry/semantic-conventions': 1.7.0 - dev: true + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false - /@opentelemetry/semantic-conventions/1.7.0: - resolution: {integrity: sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA==} + /@opentelemetry/semantic-conventions/1.8.0: + resolution: {integrity: sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew==} engines: {node: '>=14'} - dev: true - - /@panva/hkdf/1.0.2: - resolution: {integrity: sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==} + dev: false /@prisma/client/4.7.1: resolution: {integrity: sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==} @@ -1729,25 +2056,39 @@ packages: '@prisma/engines-version': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c dev: false - /@prisma/debug/4.7.0: - resolution: {integrity: sha512-KdfL70X2OgYMrweEiXa9pZReAPbsYbCbFAyT8Ud/8+w+zI1xOWpFZ4watK95ambK0f6/2p1kP6WGHGKOor1imA==} + /@prisma/client/4.7.1_prisma@4.7.0: + resolution: {integrity: sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==} + engines: {node: '>=14.17'} + requiresBuild: true + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + dependencies: + '@prisma/engines-version': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c + prisma: 4.7.0 + dev: false + + /@prisma/debug/4.7.1: + resolution: {integrity: sha512-oppjBRcTakJuAn0rANxWT9caxLKypSBT4Ajh7t+uPcO06CJOoN2Gt4VpFdw2i79U+klGAjTe/1yh8SCsTmxAhA==} dependencies: '@types/debug': 4.1.7 debug: 4.3.4 strip-ansi: 6.0.1 transitivePeerDependencies: - supports-color - dev: true + dev: false - /@prisma/engine-core/4.7.0: - resolution: {integrity: sha512-44/GcOJVP6Pa26y64a2Feg1wYKTFqZw50Tp/4rK3mlZH9il+DIHRm8NXu3wAbsKgN5YUCEdoH7ucXKgWMgecew==} + /@prisma/engine-core/4.7.1: + resolution: {integrity: sha512-8NwCQcdB4OqOjpehChtKdVn6UVuDTwZewnRG13aeK+KmZurPqGO1LhwB3dTjlfLRlbYsLGb3Xzsai7Jl5QckBA==} dependencies: - '@opentelemetry/api': 1.2.0 - '@opentelemetry/sdk-trace-base': 1.7.0_@opentelemetry+api@1.2.0 - '@prisma/debug': 4.7.0 - '@prisma/engines': 4.7.0 - '@prisma/generator-helper': 4.7.0 - '@prisma/get-platform': 4.7.0 + '@opentelemetry/api': 1.3.0 + '@opentelemetry/sdk-trace-base': 1.8.0_@opentelemetry+api@1.3.0 + '@prisma/debug': 4.7.1 + '@prisma/engines': 4.7.1 + '@prisma/generator-helper': 4.7.1 + '@prisma/get-platform': 4.7.1 chalk: 4.1.2 execa: 5.1.1 get-stream: 6.0.1 @@ -1758,7 +2099,7 @@ packages: undici: 5.11.0 transitivePeerDependencies: - supports-color - dev: true + dev: false /@prisma/engines-version/4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c: resolution: {integrity: sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==} @@ -1768,11 +2109,16 @@ packages: resolution: {integrity: sha512-afKrVFktaZ1pOK12/uFl2hRsBWIJZuC5FdDtacuKk5x/mR+rC5AbA+PlN3ZCZbmYTaeiBMHjcU5wbT5z2N3nSQ==} requiresBuild: true - /@prisma/fetch-engine/4.7.0: - resolution: {integrity: sha512-dDWcs/YpFNSFY8RvA2UfVX1YHt64glbp3clD91dBbNA1mjJSc6dRWSHJXDZQH8Pm/Zi2gT7lymDhiWjnEjOhXg==} + /@prisma/engines/4.7.1: + resolution: {integrity: sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==} + requiresBuild: true + dev: false + + /@prisma/fetch-engine/4.7.1: + resolution: {integrity: sha512-pZ3SiUV02FTMBJOwzoqGR4YcZ2jTo43Xw1QC/5YwmRkYa5NPydUnap5rvB84DgVNq5OnOrQxhWSsakOUPqcc6g==} dependencies: - '@prisma/debug': 4.7.0 - '@prisma/get-platform': 4.7.0 + '@prisma/debug': 4.7.1 + '@prisma/get-platform': 4.7.1 chalk: 4.1.2 execa: 5.1.1 find-cache-dir: 3.3.2 @@ -1791,37 +2137,37 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false - /@prisma/generator-helper/4.7.0: - resolution: {integrity: sha512-/RA7QoAPc7dTnRHULcK6c+F6t3AjKOn3XnqyY4XgmwN+IIWOhNaR4712Bid+CoK6ITyHYxgdg6B4iMscZnqXYQ==} + /@prisma/generator-helper/4.7.1: + resolution: {integrity: sha512-q9mjYOyLxkLWhqjvPB5sx5J1VRegWA2eC9mwrGgA0CSMo6SzZ7xwIe4rMJKnh4a7QMUO03+HQFdv/Nz7BOWCcg==} dependencies: - '@prisma/debug': 4.7.0 + '@prisma/debug': 4.7.1 '@types/cross-spawn': 6.0.2 chalk: 4.1.2 cross-spawn: 7.0.3 transitivePeerDependencies: - supports-color - dev: true + dev: false - /@prisma/get-platform/4.7.0: - resolution: {integrity: sha512-LmDwQ0ZidLe8ac8m8X8WkhPEDy73Dzs7s+4BXWmlfjHJsMiu7odtfa7WC2s5sAmw7Mwptpa/6XRZK08jn5zsUA==} + /@prisma/get-platform/4.7.1: + resolution: {integrity: sha512-p160ToD9j53TgSrg0O0q3GkGZTGwB30UqIu3B0bu/NIvhnhHOwuEo4tyDXYKblLOE2TL0e1t0PWOSQAvH3nKYw==} dependencies: - '@prisma/debug': 4.7.0 + '@prisma/debug': 4.7.1 transitivePeerDependencies: - supports-color - dev: true + dev: false - /@prisma/internals/4.7.0: - resolution: {integrity: sha512-6qAny0/V23p1tgSdK3+UhAd7O7NH2W3NUFlVdcQGYZgGFd8Sg6J2pWY6nC1jd8bAZy737GGFmCLr+jCmzSvneA==} + /@prisma/internals/4.7.1: + resolution: {integrity: sha512-YgG+5zLZuKdVUPnNdw9XA0XGfW1vZaOqrcGgRnla0mSIIIB3Gg3noRuHv9ZQAXeHLWh2v7MHxal6fxKElMN0ug==} dependencies: - '@prisma/debug': 4.7.0 - '@prisma/engine-core': 4.7.0 - '@prisma/engines': 4.7.0 - '@prisma/fetch-engine': 4.7.0 - '@prisma/generator-helper': 4.7.0 - '@prisma/get-platform': 4.7.0 - '@prisma/prisma-fmt-wasm': 4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635 + '@prisma/debug': 4.7.1 + '@prisma/engine-core': 4.7.1 + '@prisma/engines': 4.7.1 + '@prisma/fetch-engine': 4.7.1 + '@prisma/generator-helper': 4.7.1 + '@prisma/get-platform': 4.7.1 + '@prisma/prisma-fmt-wasm': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c archiver: 5.3.1 arg: 5.0.2 chalk: 4.1.2 @@ -1858,15 +2204,15 @@ packages: tempy: 1.0.1 terminal-link: 2.1.1 tmp: 0.2.1 - ts-pattern: 4.0.5 + ts-pattern: 4.0.6 transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false - /@prisma/prisma-fmt-wasm/4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635: - resolution: {integrity: sha512-YIjPJgDgvQfOkVYTqltw4ysL57i726xjrOPqwj9ByV0x5GtgLclXlGm8tDOoUb/WwUvdN6QhbjeQY/yABQ/+hg==} - dev: true + /@prisma/prisma-fmt-wasm/4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c: + resolution: {integrity: sha512-o9oSp2c5yDWHn9TT2Ntv3cb6LuJKPBy32gTtipYH1D166KfKBy+1RkPySobWZCKV/TrkUGlcBn5vcQgRBHPvVA==} + dev: false /@sinclair/typebox/0.24.47: resolution: {integrity: sha512-J4Xw0xYK4h7eC34MNOPQi6IkNxGRck6n4VJpWDzXIFVTW8I/D43Gf+NfWz/v/7NHlzWOPd3+T4PJ4OqklQ2u7A==} @@ -1892,7 +2238,7 @@ packages: /@tootallnate/once/2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} - dev: true + dev: false /@ts-morph/common/0.17.0: resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==} @@ -1926,8 +2272,8 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.19.6 - '@babel/types': 7.19.4 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.2 @@ -1936,14 +2282,14 @@ packages: /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.19.6 - '@babel/types': 7.19.4 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 dev: true /@types/babel__traverse/7.18.1: @@ -1955,7 +2301,7 @@ packages: /@types/babel__traverse/7.18.2: resolution: {integrity: sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==} dependencies: - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 dev: true /@types/bcryptjs/2.4.2: @@ -1968,25 +2314,32 @@ packages: /@types/cross-spawn/6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 16.11.62 - dev: true + '@types/node': 18.11.18 + dev: false /@types/debug/4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: '@types/ms': 0.7.31 + dev: false + + /@types/fs-extra/11.0.1: + resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} + dependencies: + '@types/jsonfile': 6.1.1 + '@types/node': 18.11.18 dev: true /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 16.11.62 + '@types/node': 18.11.18 dev: true /@types/is-ci/3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: - ci-info: 3.5.0 + ci-info: 3.7.1 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -2023,13 +2376,19 @@ packages: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true + /@types/jsonfile/6.1.1: + resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} + dependencies: + '@types/node': 18.11.18 + dev: true + /@types/minimist/1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true /@types/ms/0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} - dev: true + dev: false /@types/node/12.20.55: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -2042,13 +2401,15 @@ packages: resolution: {integrity: sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==} dev: true - /@types/node/16.11.62: - resolution: {integrity: sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ==} + /@types/node/18.11.13: + resolution: {integrity: sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w==} dev: true + /@types/node/18.11.18: + resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} - dev: true /@types/pluralize/0.0.29: resolution: {integrity: sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==} @@ -2062,8 +2423,28 @@ packages: resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} dev: true + /@types/prettier/2.7.2: + resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} + dev: true + + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: true + + /@types/react/18.0.26: + resolution: {integrity: sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + dev: true + /@types/retry/0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + dev: false + + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} dev: true /@types/semver/6.2.3: @@ -2082,7 +2463,7 @@ packages: resolution: {integrity: sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ==} dependencies: '@types/cookiejar': 2.1.2 - '@types/node': 16.11.62 + '@types/node': 18.11.13 dev: true /@types/supertest/2.0.12: @@ -2199,6 +2580,27 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.3: + resolution: {integrity: sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.42.0 + '@typescript-eslint/visitor-keys': 5.42.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.3 + typescript: 4.8.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.4: resolution: {integrity: sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2214,13 +2616,33 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy: + resolution: {integrity: sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.42.0 + '@typescript-eslint/types': 5.42.0 + '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4 + eslint: 8.27.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.27.0 + semver: 7.3.8 transitivePeerDependencies: - supports-color + - typescript dev: true - /@typescript-eslint/utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy: + /@typescript-eslint/utils/5.42.0_yvgxsonm4ikac5pm5z5getdq5e: resolution: {integrity: sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2230,10 +2652,10 @@ packages: '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.42.0 '@typescript-eslint/types': 5.42.0 - '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4 - eslint: 8.27.0 + '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.3 + eslint: 8.30.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.27.0 + eslint-utils: 3.0.0_eslint@8.30.0 semver: 7.3.8 transitivePeerDependencies: - supports-color @@ -2274,6 +2696,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color + dev: false /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} @@ -2281,7 +2704,7 @@ packages: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: true + dev: false /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -2302,31 +2725,22 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 - dev: true /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true - - /ansi-regex/6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: false /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: true /ansi-styles/4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles/5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -2355,7 +2769,7 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 2.3.7 - dev: true + dev: false /archiver/5.3.1: resolution: {integrity: sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==} @@ -2368,7 +2782,7 @@ packages: readdir-glob: 1.1.2 tar-stream: 2.2.0 zip-stream: 4.1.0 - dev: true + dev: false /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -2376,7 +2790,7 @@ packages: /arg/5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - dev: true + dev: false /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -2388,10 +2802,24 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /array-back/3.1.0: + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} + dev: true + + /array-back/4.0.2: + resolution: {integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==} + engines: {node: '>=8'} + dev: true + + /array-back/6.2.2: + resolution: {integrity: sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==} + engines: {node: '>=12.17'} + dev: true + /array-union/2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - dev: true /array.prototype.flat/1.3.1: resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} @@ -2399,7 +2827,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.4 + es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 dev: true @@ -2408,14 +2836,10 @@ packages: engines: {node: '>=0.10.0'} dev: true - /asap/2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: true - /astral-regex/2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} - dev: true + dev: false /async-exit-hook/2.0.1: resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} @@ -2424,17 +2848,18 @@ packages: /async/3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: true - - /asynckit/0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true + dev: false /at-least-node/1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} dev: true + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + /azure-devops-node-api/11.2.0: resolution: {integrity: sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==} dependencies: @@ -2460,17 +2885,17 @@ packages: - supports-color dev: true - /babel-jest/29.2.1_@babel+core@7.19.6: + /babel-jest/29.2.1_@babel+core@7.20.5: resolution: {integrity: sha512-gQJwArok0mqoREiCYhXKWOgUhElJj9DpnssW6GL8dG7ARYqHEhrM9fmPHTjdqEGRVXZAd6+imo3/Vwa8TjLcsw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@jest/transform': 29.2.1 '@types/babel__core': 7.1.19 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.2.0_@babel+core@7.19.6 + babel-preset-jest: 29.2.0_@babel+core@7.20.5 chalk: 4.1.2 graceful-fs: 4.2.10 slash: 3.0.0 @@ -2482,7 +2907,7 @@ packages: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -2506,7 +2931,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.19.4 + '@babel/types': 7.20.5 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.18.2 dev: true @@ -2531,24 +2956,24 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.3 dev: true - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.19.6: + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.5: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.19.6 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.19.6 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.19.6 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.19.6 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.19.6 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.19.6 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.19.6 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.6 + '@babel/core': 7.20.5 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.5 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.5 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.5 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.5 dev: true /babel-preset-jest/29.0.2_@babel+core@7.19.3: @@ -2562,15 +2987,15 @@ packages: babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.3 dev: true - /babel-preset-jest/29.2.0_@babel+core@7.19.6: + /babel-preset-jest/29.2.0_@babel+core@7.20.5: resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 babel-plugin-jest-hoist: 29.2.0 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 dev: true /balanced-match/1.0.2: @@ -2601,15 +3026,6 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.0 - dev: true - - /bl/5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.0 - dev: false /boolbase/1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -2625,6 +3041,7 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 + dev: false /braces/3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -2643,7 +3060,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001422 + caniuse-lite: 1.0.30001439 electron-to-chromium: 1.4.284 node-releases: 2.0.6 update-browserslist-db: 1.0.10_browserslist@4.21.4 @@ -2664,7 +3081,6 @@ packages: /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - dev: true /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2675,21 +3091,13 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - - /buffer/6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: false /busboy/1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: true + dev: false /call-bind/1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} @@ -2707,7 +3115,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /camelcase-keys/6.2.2: @@ -2729,14 +3137,14 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001422: - resolution: {integrity: sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==} + /caniuse-lite/1.0.30001439: + resolution: {integrity: sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==} /capital-case/1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 upper-case-first: 2.0.2 dev: false @@ -2747,7 +3155,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true /chalk/4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -2755,12 +3162,6 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true - - /chalk/5.1.2: - resolution: {integrity: sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false /change-case/4.1.2: resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} @@ -2776,7 +3177,7 @@ packages: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /char-regex/1.0.2: @@ -2800,7 +3201,7 @@ packages: uuid: 8.3.2 transitivePeerDependencies: - encoding - dev: true + dev: false /cheerio-select/2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -2826,12 +3227,29 @@ packages: parse5-htmlparser2-tree-adapter: 7.0.0 dev: true + /chevrotain-allstar/0.1.4: + resolution: {integrity: sha512-gr5PNT8keiOTt19r+XkRY19unZw7rUE/erXgfFMEJnk1ZTuiVGWREfzVPSlz9u6DxbR0zJ9NQzdQS0a/3SVKFw==} + dependencies: + chevrotain: 10.4.2 + lodash: 4.17.21 + + /chevrotain/10.4.2: + resolution: {integrity: sha512-gzF5GxE0Ckti5kZVuKEZycLntB5X2aj9RVY0r4/220GwQjdnljU+/t3kP74/FMWC7IzCDDEjQ9wsFUf0WCdSHg==} + dependencies: + '@chevrotain/cst-dts-gen': 10.4.2 + '@chevrotain/gast': 10.4.2 + '@chevrotain/types': 10.4.2 + '@chevrotain/utils': 10.4.2 + lodash: 4.17.21 + regexp-to-ast: 0.5.0 + /chevrotain/9.1.0: resolution: {integrity: sha512-A86/55so63HCfu0dgGg3j9u8uuuBOrSqly1OhBZxRu2x6sAKILLzfVjbGMw45kgier6lz45EzcjjWtTRgoT84Q==} dependencies: '@chevrotain/types': 9.1.0 '@chevrotain/utils': 9.1.0 regexp-to-ast: 0.5.0 + dev: false /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -2854,7 +3272,7 @@ packages: /ci-info/3.3.0: resolution: {integrity: sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==} - dev: true + dev: false /ci-info/3.4.0: resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==} @@ -2864,6 +3282,11 @@ packages: resolution: {integrity: sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==} dev: true + /ci-info/3.7.1: + resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} + engines: {node: '>=8'} + dev: true + /cjs-module-lexer/1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true @@ -2871,25 +3294,19 @@ packages: /clean-stack/2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} - dev: true + dev: false /cli-cursor/3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 - dev: true - - /cli-cursor/4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - restore-cursor: 4.0.0 dev: false /cli-spinners/2.7.0: resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} engines: {node: '>=6'} + dev: false /cli-truncate/2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} @@ -2897,7 +3314,7 @@ packages: dependencies: slice-ansi: 3.0.0 string-width: 4.2.3 - dev: true + dev: false /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -2945,33 +3362,42 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - dev: true /color-convert/2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name/1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /colors/1.4.0: resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} engines: {node: '>=0.1.90'} dev: false - /combined-stream/1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + /command-line-args/5.2.1: + resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + dev: true + + /command-line-usage/6.1.3: + resolution: {integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==} + engines: {node: '>=8.0.0'} dependencies: - delayed-stream: 1.0.0 + array-back: 4.0.2 + chalk: 2.4.2 + table-layout: 1.0.2 + typical: 5.2.0 dev: true /commander/6.2.1: @@ -2979,15 +3405,9 @@ packages: engines: {node: '>= 6'} dev: true - /commander/7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - dev: true - /commander/8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - dev: false /commander/9.4.1: resolution: {integrity: sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==} @@ -2996,11 +3416,7 @@ packages: /commondir/1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - dev: true - - /component-emitter/1.3.0: - resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} - dev: true + dev: false /compress-commons/4.1.1: resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==} @@ -3010,7 +3426,7 @@ packages: crc32-stream: 4.0.2 normalize-path: 3.0.0 readable-stream: 3.6.0 - dev: true + dev: false /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} @@ -3035,7 +3451,7 @@ packages: resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 upper-case: 2.0.2 dev: false @@ -3043,12 +3459,8 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true - /cookie/0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - - /cookiejar/2.1.3: - resolution: {integrity: sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==} + /convert-source-map/2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true /copy-anything/3.0.3: @@ -3058,15 +3470,27 @@ packages: is-what: 4.1.8 dev: false + /copyfiles/2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + dev: true + /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true /crc-32/1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} hasBin: true - dev: true + dev: false /crc32-stream/4.0.2: resolution: {integrity: sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==} @@ -3074,7 +3498,7 @@ packages: dependencies: crc-32: 1.2.2 readable-stream: 3.6.0 - dev: true + dev: false /create-require/1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -3103,12 +3527,11 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crypto-random-string/2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - dev: true + dev: false /css-select/5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} @@ -3125,6 +3548,10 @@ packages: engines: {node: '>= 6'} dev: true + /csstype/3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: true + /csv-generate/3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} dev: true @@ -3151,6 +3578,11 @@ packages: resolution: {integrity: sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==} dev: false + /current-module-paths/1.1.0: + resolution: {integrity: sha512-HGhLUszcgprjKmzvQoCQda8iEWsQn3sWVzPdttyJVR5cjfVDYcoyozQA5D1YXgab9v84SPMpSuD+YrPX6i1IMQ==} + engines: {node: '>=12.17'} + dev: true + /date-fns/2.29.3: resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} engines: {node: '>=0.11'} @@ -3240,12 +3672,7 @@ packages: p-map: 4.0.0 rimraf: 3.0.2 slash: 3.0.0 - dev: true - - /delayed-stream/1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: true + dev: false /detect-indent/6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} @@ -3262,13 +3689,6 @@ packages: engines: {node: '>=8'} dev: true - /dezalgo/1.0.3: - resolution: {integrity: sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==} - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - dev: true - /diff-sequences/29.0.0: resolution: {integrity: sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3279,6 +3699,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff-sequences/29.3.1: + resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -3289,7 +3714,6 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 - dev: true /doctrine/3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -3329,13 +3753,12 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /dotenv/16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dev: true /electron-to-chromium/1.4.284: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} @@ -3348,13 +3771,11 @@ packages: /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /end-of-stream/1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /enquirer/2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} @@ -3375,42 +3796,59 @@ packages: /env-paths/2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - dev: true + dev: false /error-ex/1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 - dev: true - /es-abstract/1.20.4: - resolution: {integrity: sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==} + /es-abstract/1.21.1: + resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} engines: {node: '>= 0.4'} dependencies: + available-typed-arrays: 1.0.5 call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 function-bind: 1.1.1 function.prototype.name: 1.1.5 get-intrinsic: 1.1.3 get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 has: 1.0.3 has-property-descriptors: 1.0.0 + has-proto: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.3 + internal-slot: 1.0.4 + is-array-buffer: 3.0.1 is-callable: 1.2.7 is-negative-zero: 2.0.2 is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 is-string: 1.0.7 + is-typed-array: 1.1.10 is-weakref: 1.0.2 - object-inspect: 1.12.2 + object-inspect: 1.12.3 object-keys: 1.1.1 object.assign: 4.1.4 regexp.prototype.flags: 1.4.3 safe-regex-test: 1.0.0 string.prototype.trimend: 1.0.6 string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-set-tostringtag/2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + has-tostringtag: 1.0.0 dev: true /es-shim-unscopables/1.0.0: @@ -3646,7 +4084,6 @@ packages: /escape-string-regexp/1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp/2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} @@ -3656,6 +4093,48 @@ packages: /escape-string-regexp/4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + + /eslint-plugin-jest/27.1.7_mqrelppxlcv42tihlyzz2qch5q: + resolution: {integrity: sha512-0QVzf+og4YI1Qr3UoprkqqhezAZjFffdi62b0IurkCXMqPtRW84/UT4CKsYT80h/D82LA9avjO/80Ou1LdgbaQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/utils': 5.42.0_yvgxsonm4ikac5pm5z5getdq5e + eslint: 8.30.0 + jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-jest/27.1.7_pdoqwsgoaseomjcjdinsmwbl4q: + resolution: {integrity: sha512-0QVzf+og4YI1Qr3UoprkqqhezAZjFffdi62b0IurkCXMqPtRW84/UT4CKsYT80h/D82LA9avjO/80Ou1LdgbaQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34 + '@typescript-eslint/utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy + eslint: 8.27.0 + jest: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + transitivePeerDependencies: + - supports-color + - typescript dev: true /eslint-scope/5.1.1: @@ -3684,6 +4163,16 @@ packages: eslint-visitor-keys: 2.1.0 dev: true + /eslint-utils/3.0.0_eslint@8.30.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.30.0 + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-visitor-keys/2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} @@ -3742,6 +4231,54 @@ packages: - supports-color dev: true + /eslint/8.30.0: + resolution: {integrity: sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.4.0 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.30.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.19.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.1.5 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /espree/9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3799,7 +4336,6 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /exit/0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} @@ -3833,6 +4369,17 @@ packages: jest-util: 29.2.1 dev: true + /expect/29.3.1: + resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.3.1 + jest-get-type: 29.2.0 + jest-matcher-utils: 29.3.1 + jest-message-util: 29.3.1 + jest-util: 29.3.1 + dev: true + /extendable-error/0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -3850,6 +4397,10 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true + /fast-diff/1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + /fast-glob/3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -3868,16 +4419,12 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fast-safe-stringify/2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - dev: true - /fast-write-atomic/0.2.1: resolution: {integrity: sha512-WvJe06IfNYlr+6cO3uQkdKdy3Cb1LlCJSF8zRs2eT8yuhdbSlR9nIt+TgQ92RUxiRrQm+/S7RARnMfCs5iuAjw==} - dev: true + dev: false - /fastq/1.13.0: - resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + /fastq/1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 @@ -3900,6 +4447,14 @@ packages: flat-cache: 3.0.4 dev: true + /file-set/5.1.3: + resolution: {integrity: sha512-mQ6dqz+z59on3B50IGF3ujNGbZmY1TAeLHpNfhLEeNM6Lky31w3RUlbCyqZWQs0DuZJQU4R2qDuVd9ojyzadcg==} + engines: {node: '>=12.17'} + dependencies: + array-back: 6.2.2 + glob: 7.2.3 + dev: true + /fill-range/7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -3913,6 +4468,13 @@ packages: commondir: 1.0.1 make-dir: 3.1.0 pkg-dir: 4.2.0 + dev: false + + /find-replace/3.0.0: + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 dev: true /find-up/4.1.0: @@ -3921,7 +4483,6 @@ packages: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -3929,7 +4490,6 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true /find-yarn-workspace-root2/1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} @@ -3950,35 +4510,31 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /form-data/4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - - /formidable/2.0.1: - resolution: {integrity: sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==} + /for-each/0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: - dezalgo: 1.0.3 - hexoid: 1.0.0 - once: 1.4.0 - qs: 6.9.3 + is-callable: 1.2.7 dev: true /fp-ts/2.13.1: resolution: {integrity: sha512-0eu5ULPS2c/jsa1lGFneEFFEdTbembJv8e4QKXeVJ3lm/5hyve06dlKZrpxmMwJt6rYen7sxmHHK2CLaXvWuWQ==} - dev: true + dev: false /fs-constants/1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true /fs-extra/10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: false + + /fs-extra/11.1.0: + resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} + engines: {node: '>=14.14'} dependencies: graceful-fs: 4.2.10 jsonfile: 6.1.0 @@ -4017,11 +4573,10 @@ packages: resolution: {integrity: sha512-Xn4fDhLydXkuzepZVsr02jakLlmoARPy+YWIclo4kh0GyNGUHnTqeH/w/qIsVn50dFxtp8otPL2t/HcPJBbxUA==} dependencies: minimatch: 5.1.0 - dev: true + dev: false /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} @@ -4033,7 +4588,6 @@ packages: /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true /function.prototype.name/1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} @@ -4041,7 +4595,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.4 + es-abstract: 1.21.1 functions-have-names: 1.2.3 dev: true @@ -4075,7 +4629,6 @@ packages: /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} @@ -4111,14 +4664,12 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true /global-dirs/3.0.0: resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} engines: {node: '>=10'} dependencies: ini: 2.0.0 - dev: true /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -4132,6 +4683,20 @@ packages: type-fest: 0.20.2 dev: true + /globals/13.19.0: + resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis/1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.1.4 + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -4139,14 +4704,18 @@ packages: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.2.12 - ignore: 5.2.0 + ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 + + /gopd/1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.1.3 dev: true /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true /grapheme-splitter/1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} @@ -4164,12 +4733,10 @@ packages: /has-flag/3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag/4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-property-descriptors/1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} @@ -4177,6 +4744,11 @@ packages: get-intrinsic: 1.1.3 dev: true + /has-proto/1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + /has-symbols/1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} @@ -4192,14 +4764,13 @@ packages: /has-yarn/2.1.0: resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} engines: {node: '>=8'} - dev: true + dev: false /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 - dev: true /hasha/5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} @@ -4207,23 +4778,17 @@ packages: dependencies: is-stream: 2.0.1 type-fest: 0.8.1 - dev: true + dev: false /header-case/2.0.4: resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} dependencies: capital-case: 1.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false - /hexoid/1.0.0: - resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} - engines: {node: '>=8'} - dev: true - /hosted-git-info/2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - dev: true /hosted-git-info/4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} @@ -4254,7 +4819,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color - dev: true + dev: false /https-proxy-agent/5.0.0: resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} @@ -4274,7 +4839,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color - dev: true + dev: false /human-id/1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} @@ -4283,7 +4848,6 @@ packages: /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /iconv-lite/0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -4300,6 +4864,10 @@ packages: engines: {node: '>= 4'} dev: true + /ignore/5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + /import-fresh/3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -4325,14 +4893,12 @@ packages: /indent-string/4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - dev: true /inflight/1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4344,10 +4910,9 @@ packages: /ini/2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} - dev: true - /internal-slot/1.0.3: - resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + /internal-slot/1.0.4: + resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.1.3 @@ -4355,9 +4920,16 @@ packages: side-channel: 1.0.4 dev: true + /is-array-buffer/3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-typed-array: 1.1.10 + dev: true + /is-arrayish/0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true /is-bigint/1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -4389,14 +4961,13 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true dependencies: - ci-info: 3.5.0 + ci-info: 3.7.1 dev: true /is-core-module/2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 - dev: true /is-date-object/1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -4409,7 +4980,7 @@ packages: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true - dev: true + dev: false /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -4418,7 +4989,6 @@ packages: /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-generator-fn/2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} @@ -4434,11 +5004,6 @@ packages: /is-interactive/1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - dev: true - - /is-interactive/2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} dev: false /is-negative-zero/2.0.2: @@ -4460,12 +5025,11 @@ packages: /is-path-cwd/2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} engines: {node: '>=6'} - dev: true + dev: false /is-path-inside/3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - dev: true /is-plain-obj/1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} @@ -4489,7 +5053,6 @@ packages: /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} @@ -4512,14 +5075,20 @@ packages: has-symbols: 1.0.3 dev: true + /is-typed-array/1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + /is-unicode-supported/0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - dev: true - - /is-unicode-supported/1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} dev: false /is-weakref/1.0.2: @@ -4536,22 +5105,23 @@ packages: /is-windows/1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} - dev: true /is-wsl/2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} dependencies: is-docker: 2.2.1 + dev: false + + /isarray/0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} dev: true /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /istanbul-lib-coverage/3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} @@ -4575,8 +5145,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.19.6 - '@babel/parser': 7.19.6 + '@babel/core': 7.20.5 + '@babel/parser': 7.20.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -4636,7 +5206,7 @@ packages: '@jest/expect': 29.0.3 '@jest/test-result': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -4663,7 +5233,7 @@ packages: '@jest/expect': 29.2.1 '@jest/test-result': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -4778,7 +5348,7 @@ packages: - supports-color dev: true - /jest-config/29.0.3_uo4il2aklsrxuk4ro37qmnu2ge: + /jest-config/29.0.3_sfpnjnnyjha3fwltwg26xa3eti: resolution: {integrity: sha512-U5qkc82HHVYe3fNu2CRXLN4g761Na26rWKf7CjM8LlZB3In1jadEkZdMwsE37rd9RSPV0NfYaCjHdk/gu3v+Ew==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -4793,7 +5363,7 @@ packages: '@babel/core': 7.19.3 '@jest/test-sequencer': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 babel-jest: 29.0.3_@babel+core@7.19.3 chalk: 4.1.2 ci-info: 3.4.0 @@ -4830,11 +5400,11 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@jest/test-sequencer': 29.2.1 '@jest/types': 29.2.1 '@types/node': 14.18.32 - babel-jest: 29.2.1_@babel+core@7.19.6 + babel-jest: 29.2.1_@babel+core@7.20.5 chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 @@ -4858,7 +5428,7 @@ packages: - supports-color dev: true - /jest-config/29.2.1_uo4il2aklsrxuk4ro37qmnu2ge: + /jest-config/29.2.1_sfpnjnnyjha3fwltwg26xa3eti: resolution: {integrity: sha512-EV5F1tQYW/quZV2br2o88hnYEeRzG53Dfi6rSG3TZBuzGQ6luhQBux/RLlU5QrJjCdq3LXxRRM8F1LP6DN1ycA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -4870,11 +5440,11 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@jest/test-sequencer': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 - babel-jest: 29.2.1_@babel+core@7.19.6 + '@types/node': 18.11.13 + babel-jest: 29.2.1_@babel+core@7.20.5 chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 @@ -4918,6 +5488,16 @@ packages: pretty-format: 29.2.1 dev: true + /jest-diff/29.3.1: + resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.3.1 + jest-get-type: 29.2.0 + pretty-format: 29.3.1 + dev: true + /jest-docblock/29.0.0: resolution: {integrity: sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4961,7 +5541,7 @@ packages: '@jest/environment': 29.0.3 '@jest/fake-timers': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-mock: 29.0.3 jest-util: 29.0.3 dev: true @@ -4973,7 +5553,7 @@ packages: '@jest/environment': 29.2.1 '@jest/fake-timers': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-mock: 29.2.1 jest-util: 29.2.1 dev: true @@ -5003,7 +5583,7 @@ packages: dependencies: '@jest/types': 29.0.3 '@types/graceful-fs': 4.1.5 - '@types/node': 16.11.62 + '@types/node': 18.11.13 anymatch: 3.1.2 fb-watchman: 2.0.2 graceful-fs: 4.2.10 @@ -5022,7 +5602,7 @@ packages: dependencies: '@jest/types': 29.2.1 '@types/graceful-fs': 4.1.5 - '@types/node': 16.11.62 + '@types/node': 18.11.13 anymatch: 3.1.2 fb-watchman: 2.0.2 graceful-fs: 4.2.10 @@ -5035,6 +5615,25 @@ packages: fsevents: 2.3.2 dev: true + /jest-haste-map/29.3.1: + resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.3.1 + '@types/graceful-fs': 4.1.5 + '@types/node': 18.11.18 + anymatch: 3.1.2 + fb-watchman: 2.0.2 + graceful-fs: 4.2.10 + jest-regex-util: 29.2.0 + jest-util: 29.3.1 + jest-worker: 29.3.1 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /jest-leak-detector/29.0.3: resolution: {integrity: sha512-YfW/G63dAuiuQ3QmQlh8hnqLDe25WFY3eQhuc/Ev1AGmkw5zREblTh7TCSKLoheyggu6G9gxO2hY8p9o6xbaRQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5071,6 +5670,16 @@ packages: pretty-format: 29.2.1 dev: true + /jest-matcher-utils/29.3.1: + resolution: {integrity: sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.3.1 + jest-get-type: 29.2.0 + pretty-format: 29.3.1 + dev: true + /jest-message-util/29.0.3: resolution: {integrity: sha512-7T8JiUTtDfppojosORAflABfLsLKMLkBHSWkjNQrjIltGoDzNGn7wEPOSfjqYAGTYME65esQzMJxGDjuLBKdOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5101,12 +5710,27 @@ packages: stack-utils: 2.0.5 dev: true + /jest-message-util/29.3.1: + resolution: {integrity: sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 29.3.1 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 29.3.1 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + /jest-mock/29.0.3: resolution: {integrity: sha512-ort9pYowltbcrCVR43wdlqfAiFJXBx8l4uJDsD8U72LgBcetvEp+Qxj1W9ZYgMRoeAo+ov5cnAGF2B6+Oth+ww==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 dev: true /jest-mock/29.2.1: @@ -5114,10 +5738,19 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-util: 29.2.1 dev: true + /jest-mock/29.3.1: + resolution: {integrity: sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.3.1 + '@types/node': 18.11.18 + jest-util: 29.3.1 + dev: true + /jest-pnp-resolver/1.2.2_jest-resolve@29.0.3: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} @@ -5211,7 +5844,7 @@ packages: '@jest/test-result': 29.0.3 '@jest/transform': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 emittery: 0.10.2 graceful-fs: 4.2.10 @@ -5240,7 +5873,7 @@ packages: '@jest/test-result': 29.2.1 '@jest/transform': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 emittery: 0.10.2 graceful-fs: 4.2.10 @@ -5266,12 +5899,12 @@ packages: dependencies: '@jest/environment': 29.0.3 '@jest/fake-timers': 29.0.3 - '@jest/globals': 29.0.3 + '@jest/globals': 29.3.1 '@jest/source-map': 29.0.0 '@jest/test-result': 29.0.3 '@jest/transform': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -5301,7 +5934,7 @@ packages: '@jest/test-result': 29.2.1 '@jest/transform': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -5347,7 +5980,7 @@ packages: jest-util: 29.0.3 natural-compare: 1.4.0 pretty-format: 29.0.3 - semver: 7.3.7 + semver: 7.3.8 transitivePeerDependencies: - supports-color dev: true @@ -5356,10 +5989,10 @@ packages: resolution: {integrity: sha512-KZdLD7iEz5M4ZYd+ezZ/kk73z+DtNbk/yJ4Qx7408Vb0CCuclJIZPa/HmIwSsCfIlOBNcYTKufr7x/Yv47oYlg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 '@babel/generator': 7.19.6 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.19.6 - '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.19.6 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 + '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.20.5 '@babel/traverse': 7.19.6 '@babel/types': 7.19.4 '@jest/expect-utils': 29.2.1 @@ -5367,7 +6000,7 @@ packages: '@jest/types': 29.2.1 '@types/babel__traverse': 7.18.2 '@types/prettier': 2.7.1 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 chalk: 4.1.2 expect: 29.2.1 graceful-fs: 4.2.10 @@ -5384,12 +6017,44 @@ packages: - supports-color dev: true + /jest-snapshot/29.3.1: + resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.20.5 + '@babel/generator': 7.20.5 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 + '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.5 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 + '@jest/expect-utils': 29.3.1 + '@jest/transform': 29.3.1 + '@jest/types': 29.3.1 + '@types/babel__traverse': 7.18.2 + '@types/prettier': 2.7.2 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 + chalk: 4.1.2 + expect: 29.3.1 + graceful-fs: 4.2.10 + jest-diff: 29.3.1 + jest-get-type: 29.2.0 + jest-haste-map: 29.3.1 + jest-matcher-utils: 29.3.1 + jest-message-util: 29.3.1 + jest-util: 29.3.1 + natural-compare: 1.4.0 + pretty-format: 29.3.1 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + /jest-util/29.0.3: resolution: {integrity: sha512-Q0xaG3YRG8QiTC4R6fHjHQPaPpz9pJBEi0AeOE4mQh/FuWOijFjGXMMOfQEaU9i3z76cNR7FobZZUQnL6IyfdQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 ci-info: 3.4.0 graceful-fs: 4.2.10 @@ -5401,13 +6066,25 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 chalk: 4.1.2 ci-info: 3.5.0 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true + /jest-util/29.3.1: + resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.3.1 + '@types/node': 18.11.18 + chalk: 4.1.2 + ci-info: 3.7.1 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + /jest-validate/29.0.3: resolution: {integrity: sha512-OebiqqT6lK8cbMPtrSoS3aZP4juID762lZvpf1u+smZnwTEBCBInan0GAIIhv36MxGaJvmq5uJm7dl5gVt+Zrw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5438,7 +6115,7 @@ packages: dependencies: '@jest/test-result': 29.0.3 '@jest/types': 29.0.3 - '@types/node': 16.11.62 + '@types/node': 18.11.13 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.10.2 @@ -5452,7 +6129,7 @@ packages: dependencies: '@jest/test-result': 29.2.1 '@jest/types': 29.2.1 - '@types/node': 16.11.62 + '@types/node': 18.11.13 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.10.2 @@ -5464,7 +6141,7 @@ packages: resolution: {integrity: sha512-Tl/YWUugQOjoTYwjKdfJWkSOfhufJHO5LhXTSZC3TRoQKO+fuXnZAdoXXBlpLXKGODBL3OvdUasfDD4PcMe6ng==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 16.11.62 + '@types/node': 18.11.13 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -5473,12 +6150,22 @@ packages: resolution: {integrity: sha512-ROHTZ+oj7sBrgtv46zZ84uWky71AoYi0vEV9CdEtc1FQunsoAGe5HbQmW76nI5QWdvECVPrSi1MCVUmizSavMg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 16.11.62 + '@types/node': 18.11.13 jest-util: 29.2.1 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true + /jest-worker/29.3.1: + resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 18.11.18 + jest-util: 29.3.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + /jest/29.0.3_johvxhudwcpndp4mle25vwrlq4: resolution: {integrity: sha512-ElgUtJBLgXM1E8L6K1RW1T96R897YY/3lRYqq9uVcPWtP2AAl/nQ16IYDh/FzQOOQ12VEuLdcPU83mbhG2C3PQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5519,9 +6206,6 @@ packages: - ts-node dev: true - /jose/4.11.1: - resolution: {integrity: sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==} - /js-sdsl/4.1.5: resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} dev: true @@ -5552,7 +6236,6 @@ packages: /json-parse-even-better-errors/2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -5568,6 +6251,12 @@ packages: hasBin: true dev: true + /json5/2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + /jsonfile/4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: @@ -5580,7 +6269,6 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.10 - dev: true /jsonschema/1.4.1: resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} @@ -5602,41 +6290,41 @@ packages: /kleur/3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - dev: true /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} dev: true - /langium-cli/0.5.0: - resolution: {integrity: sha512-HhJOGuEyTnaaU5oE7X6OoeAWhJw6AsaZGOyNUYUukpP75/m/NvAfMBSSrbY21Os5eXaO8X+xad5lRC3ld6TBWQ==} + /langium-cli/1.0.0: + resolution: {integrity: sha512-F5RZu3/PrlRfebMM6irtHDk2cj5PdUzM4y3M5kNu8k5IC3gIhc4QN+aDD2XHYz8hQSA+bCRUIDwCdhy/7VN4cg==} engines: {node: '>=12.0.0'} hasBin: true dependencies: chalk: 4.1.2 - commander: 7.2.0 + commander: 8.3.0 fs-extra: 9.1.0 jsonschema: 1.4.1 - langium: 0.5.0 + langium: 1.0.1 lodash: 4.17.21 dev: true - /langium/0.5.0: - resolution: {integrity: sha512-7mheQQtyyPm8r9R+8sPZopcdzEcHtCC+M52m8ZbotzNql8RPLs3+QbDY6EZ98ejKtJCGt9RviTkfZEt1LxX86w==} - engines: {node: '>=12.0.0'} + /langium/1.0.1: + resolution: {integrity: sha512-9w5NRsspYOOXV56q1EhXeFJWkboAVKZDzIEqPLraMQPQy6fvq104wlVwgvF6w9H4IcCpDHCsHJLsfzQMWBsjAA==} + engines: {node: '>=14.0.0'} dependencies: - chevrotain: 9.1.0 + chevrotain: 10.4.2 + chevrotain-allstar: 0.1.4 vscode-languageserver: 8.0.2 vscode-languageserver-textdocument: 1.0.7 - vscode-uri: 3.0.6 + vscode-uri: 3.0.7 /lazystream/1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} dependencies: readable-stream: 2.3.7 - dev: true + dev: false /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -5653,7 +6341,6 @@ packages: /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true /linkify-it/3.0.3: resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} @@ -5661,6 +6348,13 @@ packages: uc.micro: 1.0.6 dev: true + /load-module/4.2.1: + resolution: {integrity: sha512-Sbfg6R4LjvyThJpqUoADHMjyoI2+cL4msbCQeZ9kkY/CqP/TT2938eftKm7x4I2gd4/A+DEe6nePkbfWYbXwSw==} + engines: {node: '>=12.17'} + dependencies: + array-back: 6.2.2 + dev: true + /load-yaml-file/0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} @@ -5676,30 +6370,32 @@ packages: engines: {node: '>=8'} dependencies: p-locate: 4.1.0 - dev: true /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 + + /lodash.camelcase/4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true /lodash.defaults/4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: true + dev: false /lodash.difference/4.5.0: resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true + dev: false /lodash.flatten/4.4.0: resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true + dev: false /lodash.isplainobject/4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: true + dev: false /lodash.memoize/4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -5715,11 +6411,10 @@ packages: /lodash.union/4.6.0: resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true + dev: false /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /log-symbols/4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -5727,14 +6422,6 @@ packages: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - - /log-symbols/5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - dependencies: - chalk: 5.1.2 - is-unicode-supported: 1.3.0 dev: false /loose-envify/1.4.0: @@ -5746,7 +6433,7 @@ packages: /lower-case/2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /lru-cache/4.1.5: @@ -5767,7 +6454,6 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.0 - dev: true /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -5823,17 +6509,11 @@ packages: /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /methods/1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - dev: true - /micromatch/4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -5841,30 +6521,12 @@ packages: braces: 3.0.2 picomatch: 2.3.1 - /mime-db/1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: true - - /mime-types/2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: true - /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} hasBin: true dev: true - /mime/2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - dev: true - /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -5877,7 +6539,6 @@ packages: /min-indent/1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - dev: true /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -5889,6 +6550,7 @@ packages: engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 + dev: false /minimist-options/4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} @@ -5925,14 +6587,13 @@ packages: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - dev: false /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} /ms/2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true + dev: false /mute-stream/0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -5963,34 +6624,9 @@ packages: /new-github-issue-url/0.2.1: resolution: {integrity: sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA==} engines: {node: '>=10'} - dev: true - - /next-auth/4.17.0_azq6kxkn3od7qdylwkyksrwopy: - resolution: {integrity: sha512-aN2tdnjS0MDeUpB2tBDOaWnegkgeMWrsccujbXRGMJ607b+EwRcy63MFGSr0OAboDJEe0902piXQkt94GqF8Qw==} - engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0} - peerDependencies: - next: ^12.2.5 || ^13 - nodemailer: ^6.6.5 - react: ^17.0.2 || ^18 - react-dom: ^17.0.2 || ^18 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@babel/runtime': 7.20.1 - '@panva/hkdf': 1.0.2 - cookie: 0.5.0 - jose: 4.11.1 - next: 12.3.1_biqbaboplfbrettd7655fr4n2y - oauth: 0.9.15 - openid-client: 5.3.1 - preact: 10.11.3 - preact-render-to-string: 5.2.6_preact@10.11.3 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - uuid: 8.3.2 + dev: false - /next/12.3.1_6tziyx3dehkoeijunclpkpolha: + /next/12.3.1_672uxklweod7ene3nqtsh262ca: resolution: {integrity: sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==} engines: {node: '>=12.22.0'} hasBin: true @@ -6010,11 +6646,11 @@ packages: dependencies: '@next/env': 12.3.1 '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001422 + caniuse-lite: 1.0.30001439 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - styled-jsx: 5.0.7_b6k74wvxdvqypha4emuv7fd2ke + styled-jsx: 5.0.7_zavbqmrropwrojvx6ojaa4s7im use-sync-external-store: 1.2.0_react@18.2.0 optionalDependencies: '@next/swc-android-arm-eabi': 12.3.1 @@ -6055,7 +6691,7 @@ packages: dependencies: '@next/env': 12.3.1 '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001422 + caniuse-lite: 1.0.30001439 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -6078,12 +6714,13 @@ packages: transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + dev: false /no-case/3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /node-abi/3.28.0: @@ -6107,7 +6744,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -6121,6 +6757,13 @@ packages: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} dev: true + /noms/0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + dev: true + /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -6128,19 +6771,16 @@ packages: resolve: 1.22.1 semver: 5.7.1 validate-npm-package-license: 3.0.4 - dev: true /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /npm-run-path/4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /nth-check/2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -6148,15 +6788,8 @@ packages: boolbase: 1.0.0 dev: true - /oauth/0.9.15: - resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} - - /object-hash/2.2.0: - resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} - engines: {node: '>= 6'} - - /object-inspect/1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + /object-inspect/1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} dev: true /object-keys/1.1.1: @@ -6174,15 +6807,10 @@ packages: object-keys: 1.1.1 dev: true - /oidc-token-hash/5.0.1: - resolution: {integrity: sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==} - engines: {node: ^10.13.0 || >=12.0.0} - /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime/5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -6195,16 +6823,8 @@ packages: engines: {node: '>=8'} dependencies: is-docker: 2.2.1 - is-wsl: 2.2.0 - dev: true - - /openid-client/5.3.1: - resolution: {integrity: sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw==} - dependencies: - jose: 4.11.1 - lru-cache: 6.0.0 - object-hash: 2.2.0 - oidc-token-hash: 5.0.1 + is-wsl: 2.2.0 + dev: false /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} @@ -6231,21 +6851,6 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - - /ora/6.1.2: - resolution: {integrity: sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - bl: 5.1.0 - chalk: 5.1.2 - cli-cursor: 4.0.0 - cli-spinners: 2.7.0 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - strip-ansi: 7.0.1 - wcwidth: 1.0.1 dev: false /os-tmpdir/1.0.2: @@ -6262,47 +6867,41 @@ packages: engines: {node: '>=8'} dependencies: p-map: 2.1.0 - dev: true /p-limit/2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} dependencies: p-try: 2.2.0 - dev: true /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 - dev: true /p-locate/4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} dependencies: p-limit: 2.3.0 - dev: true /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 - dev: true /p-map/2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} - dev: true /p-map/4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} dependencies: aggregate-error: 3.1.0 - dev: true + dev: false /p-retry/4.6.2: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} @@ -6310,18 +6909,17 @@ packages: dependencies: '@types/retry': 0.12.0 retry: 0.13.1 - dev: true + dev: false /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - dev: true /param-case/3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /parent-module/1.0.1: @@ -6339,7 +6937,6 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true /parse-semver/1.1.1: resolution: {integrity: sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==} @@ -6364,7 +6961,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /path-browserify/1.0.1: @@ -6375,32 +6972,27 @@ packages: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} dependencies: dot-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-is-absolute/1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - dev: true /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true /pend/1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -6428,7 +7020,6 @@ packages: engines: {node: '>=8'} dependencies: find-up: 4.1.0 - dev: true /plimit-lit/1.4.1: resolution: {integrity: sha512-bK14ePAod0XWhXwjT6XvYfjcQ9PbCUkZXnDCAKRMZTJCaDIV9VFya1S/I+3WSbpdR8uBhCDh8TS4lQ/JQvhNFA==} @@ -6449,17 +7040,6 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /preact-render-to-string/5.2.6_preact@10.11.3: - resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} - peerDependencies: - preact: '>=10' - dependencies: - preact: 10.11.3 - pretty-format: 3.8.0 - - /preact/10.11.3: - resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} - /prebuild-install/7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} @@ -6494,11 +7074,10 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier/2.8.0: - resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + /prettier/2.8.3: + resolution: {integrity: sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==} engines: {node: '>=10.13.0'} hasBin: true - dev: true /pretty-format/29.0.3: resolution: {integrity: sha512-cHudsvQr1K5vNVLbvYF/nv3Qy/F/BcEKxGuIeMiVMRHxPOO1RxXooP8g/ZrwAp7Dx+KdMZoOc7NxLHhMrP2f9Q==} @@ -6518,8 +7097,20 @@ packages: react-is: 18.2.0 dev: true - /pretty-format/3.8.0: - resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + /pretty-format/29.3.1: + resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /printj/1.3.1: + resolution: {integrity: sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==} + engines: {node: '>=0.8'} + hasBin: true + dev: true /prisma/4.7.0: resolution: {integrity: sha512-VsecNo0Ca3+bDTzSpJqIpdupKVhhQ8aOYeWc09JlUM89knqvhSrlMrg0U8BiOD4tFrY1OPaCcraK8leDBxKMBg==} @@ -6528,16 +7119,14 @@ packages: requiresBuild: true dependencies: '@prisma/engines': 4.7.0 - dev: false /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /progress/2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} - dev: true + dev: false /promise-polyfill/8.2.3: resolution: {integrity: sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==} @@ -6555,7 +7144,6 @@ packages: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 - dev: true /pseudomap/1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} @@ -6580,11 +7168,6 @@ packages: side-channel: 1.0.4 dev: true - /qs/6.9.3: - resolution: {integrity: sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==} - engines: {node: '>=0.6'} - dev: true - /queue-lit/1.4.0: resolution: {integrity: sha512-l1+4YHm4vHWpCnvTg8JMsnPETmPvLGWhqjvNOc8TSbqscGplHVSWXOxybA3vYeMNNIR9Z1PQt85U+S3wFJX2uQ==} dev: true @@ -6633,7 +7216,6 @@ packages: find-up: 4.1.0 read-pkg: 5.2.0 type-fest: 0.8.1 - dev: true /read-pkg/5.2.0: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} @@ -6643,7 +7225,6 @@ packages: normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 - dev: true /read-yaml-file/1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} @@ -6662,6 +7243,15 @@ packages: mute-stream: 0.0.8 dev: true + /readable-stream/1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + dev: true + /readable-stream/2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} dependencies: @@ -6672,7 +7262,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream/3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} @@ -6686,7 +7275,7 @@ packages: resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==} dependencies: minimatch: 5.1.0 - dev: true + dev: false /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -6703,8 +7292,14 @@ packages: strip-indent: 3.0.0 dev: true + /reduce-flatten/2.0.0: + resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} + engines: {node: '>=6'} + dev: true + /regenerator-runtime/0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: true /regexp-to-ast/0.5.0: resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} @@ -6723,10 +7318,29 @@ packages: engines: {node: '>=8'} dev: true + /renamer/4.0.0: + resolution: {integrity: sha512-yurufcXxbJfFBVAUoByNyDVH811zTZ/MrKo6gUH8pHGeAmdK7J5egj2lSNe57HuVIvnVzSalzeVGu8pi8UHGxg==} + engines: {node: '>=12.17'} + hasBin: true + dependencies: + array-back: 6.2.2 + chalk: 4.1.2 + command-line-args: 5.2.1 + command-line-usage: 6.1.3 + current-module-paths: 1.1.0 + fast-diff: 1.2.0 + file-set: 5.1.3 + global-dirs: 3.0.0 + load-module: 4.2.1 + printj: 1.3.1 + stream-read-all: 3.0.1 + typical: 7.1.1 + dev: true + /replace-string/3.1.0: resolution: {integrity: sha512-yPpxc4ZR2makceA9hy/jHNqc7QVkd4Je/N0WRHm6bs3PtivPuPynxE5ejU/mp5EhnCv8+uZL7vhz8rkluSlx+Q==} engines: {node: '>=8'} - dev: true + dev: false /require-directory/2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -6766,19 +7380,10 @@ packages: is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /restore-cursor/3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: true - - /restore-cursor/4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: onetime: 5.1.2 signal-exit: 3.0.7 @@ -6787,7 +7392,7 @@ packages: /retry/0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - dev: true + dev: false /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} @@ -6798,7 +7403,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -6808,12 +7412,11 @@ packages: /rxjs/7.5.7: resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: true /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -6842,20 +7445,10 @@ packages: /semver/5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true - dev: true /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true - dev: true - - /semver/7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true /semver/7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} @@ -6868,7 +7461,7 @@ packages: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 upper-case-first: 2.0.2 dev: false @@ -6888,7 +7481,6 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex/1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} @@ -6898,7 +7490,6 @@ packages: /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shell-quote/1.7.4: resolution: {integrity: sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==} @@ -6909,7 +7500,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.1.3 - object-inspect: 1.12.2 + object-inspect: 1.12.3 dev: true /signal-exit/3.0.7: @@ -6929,12 +7520,10 @@ packages: /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - dev: true /sleep-promise/9.1.0: resolution: {integrity: sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==} @@ -6947,7 +7536,7 @@ packages: ansi-styles: 4.3.0 astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - dev: true + dev: false /smartwrap/2.0.2: resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} @@ -6966,7 +7555,7 @@ packages: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: dot-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /source-map-js/1.0.2: @@ -7001,22 +7590,18 @@ packages: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.12 - dev: true /spdx-exceptions/2.3.0: resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} - dev: true /spdx-expression-parse/3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.3.0 spdx-license-ids: 3.0.12 - dev: true /spdx-license-ids/3.0.12: resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} - dev: true /sprintf-js/1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -7029,6 +7614,11 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stream-read-all/3.0.1: + resolution: {integrity: sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==} + engines: {node: '>=10'} + dev: true + /stream-transform/2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: @@ -7038,7 +7628,7 @@ packages: /streamsearch/1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - dev: true + dev: false /string-length/4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -7055,14 +7645,13 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string.prototype.trimend/1.0.6: resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.4 + es-abstract: 1.21.1 dev: true /string.prototype.trimstart/1.0.6: @@ -7070,14 +7659,17 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.4 + es-abstract: 1.21.1 + dev: true + + /string_decoder/0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} dev: true /string_decoder/1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder/1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -7089,14 +7681,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true - - /strip-ansi/7.0.1: - resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} - engines: {node: '>=12'} - dependencies: - ansi-regex: 6.0.1 - dev: false /strip-bom/3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -7111,14 +7695,12 @@ packages: /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-indent/3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} dependencies: min-indent: 1.0.1 - dev: true /strip-json-comments/2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} @@ -7130,7 +7712,7 @@ packages: engines: {node: '>=8'} dev: true - /styled-jsx/5.0.7_b6k74wvxdvqypha4emuv7fd2ke: + /styled-jsx/5.0.7_react@18.2.0: resolution: {integrity: sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==} engines: {node: '>= 12.0.0'} peerDependencies: @@ -7143,11 +7725,10 @@ packages: babel-plugin-macros: optional: true dependencies: - '@babel/core': 7.19.3 react: 18.2.0 - dev: true + dev: false - /styled-jsx/5.0.7_react@18.2.0: + /styled-jsx/5.0.7_zavbqmrropwrojvx6ojaa4s7im: resolution: {integrity: sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==} engines: {node: '>= 12.0.0'} peerDependencies: @@ -7160,25 +7741,8 @@ packages: babel-plugin-macros: optional: true dependencies: + '@babel/core': 7.20.5 react: 18.2.0 - - /superagent/8.0.2: - resolution: {integrity: sha512-QtYZ9uaNAMexI7XWl2vAXAh0j4q9H7T0WVEI/y5qaUB3QLwxo+voUgCQ217AokJzUTIVOp0RTo7fhZrwhD7A2Q==} - engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please use v8.0.0 until https://github.com/visionmedia/superagent/issues/1743 is resolved - dependencies: - component-emitter: 1.3.0 - cookiejar: 2.1.3 - debug: 4.3.4 - fast-safe-stringify: 2.1.1 - form-data: 4.0.0 - formidable: 2.0.1 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.11.0 - semver: 7.3.7 - transitivePeerDependencies: - - supports-color dev: true /superjson/1.11.0: @@ -7188,29 +7752,24 @@ packages: copy-anything: 3.0.3 dev: false - /supertest/6.3.0: - resolution: {integrity: sha512-QgWju1cNoacP81Rv88NKkQ4oXTzGg0eNZtOoxp1ROpbS4OHY/eK5b8meShuFtdni161o5X0VQvgo7ErVyKK+Ow==} - engines: {node: '>=6.4.0'} + /superjson/1.12.1: + resolution: {integrity: sha512-HMTj43zvwW5bD+JCZCvFf4DkZQCmiLTen4C+W1Xogj0SPOpnhxsriogM04QmBVGH5b3kcIIOr6FqQ/aoIDx7TQ==} + engines: {node: '>=10'} dependencies: - methods: 1.1.2 - superagent: 8.0.2 - transitivePeerDependencies: - - supports-color - dev: true + copy-anything: 3.0.3 + dev: false /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color/7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-color/8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} @@ -7225,12 +7784,10 @@ packages: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - dev: true /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true /swr/1.3.0_react@18.2.0: resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==} @@ -7240,6 +7797,16 @@ packages: react: 18.2.0 dev: false + /table-layout/1.0.2: + resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==} + engines: {node: '>=8.0.0'} + dependencies: + array-back: 4.0.2 + deep-extend: 0.6.0 + typical: 5.2.0 + wordwrapjs: 4.0.1 + dev: true + /tar-fs/2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: @@ -7258,17 +7825,16 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.0 - dev: true /temp-dir/1.0.0: resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} engines: {node: '>=4'} - dev: true + dev: false /temp-dir/2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} - dev: true + dev: false /temp-write/4.0.0: resolution: {integrity: sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw==} @@ -7279,7 +7845,7 @@ packages: make-dir: 3.1.0 temp-dir: 1.0.0 uuid: 3.4.0 - dev: true + dev: false /tempy/1.0.1: resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==} @@ -7290,7 +7856,7 @@ packages: temp-dir: 2.0.0 type-fest: 0.16.0 unique-string: 2.0.0 - dev: true + dev: false /term-size/2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} @@ -7303,7 +7869,6 @@ packages: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - dev: true /test-exclude/6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -7318,6 +7883,13 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /through2/2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + dependencies: + readable-stream: 2.3.7 + xtend: 4.0.2 + dev: true + /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -7330,7 +7902,6 @@ packages: engines: {node: '>=8.17.0'} dependencies: rimraf: 3.0.2 - dev: true /tmpl/1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -7349,7 +7920,6 @@ packages: /tr46/0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /tree-kill/1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} @@ -7361,7 +7931,7 @@ packages: engines: {node: '>=8'} dev: true - /ts-jest/29.0.1_poggjixajg6vd6yquly7s7dsj4: + /ts-jest/29.0.1_57hartw4ijnqan4zaqtggk6uaa: resolution: {integrity: sha512-htQOHshgvhn93QLxrmxpiQPk69+M1g7govO1g6kf6GsjCv4uvRV0znVmDrrvjUrVCnTYeY4FBxTYYYD4airyJA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -7382,7 +7952,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.19.3 + '@babel/core': 7.20.5 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 @@ -7390,12 +7960,12 @@ packages: json5: 2.2.1 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.3.7 + semver: 7.3.8 typescript: 4.8.3 yargs-parser: 21.1.1 dev: true - /ts-jest/29.0.3_nvckv3qbfhmmsla6emqlkyje4a: + /ts-jest/29.0.3_yf6yylffq7wy4s6j6no7bitnpa: resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -7416,7 +7986,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.19.6 + '@babel/core': 7.20.5 bs-logger: 0.2.6 esbuild: 0.15.12 fast-json-stable-stringify: 2.1.0 @@ -7499,9 +8069,9 @@ packages: yn: 3.1.1 dev: true - /ts-pattern/4.0.5: - resolution: {integrity: sha512-Bq44KCEt7JVaNLa148mBCJkcQf4l7jtLEBDuDdeuLynWDA+1a60P4D0rMkqSM9mOKLQbIWUddE9h3XKyKwBeqA==} - dev: true + /ts-pattern/4.0.6: + resolution: {integrity: sha512-sFHQYD4KoysBi7e7a2mzDPvRBeqA4w+vEyRE+P5MU9VLq8eEYxgKCgD9RNEAT+itGRWUTYN+hry94GDPLb1/Yw==} + dev: false /tsc-alias/1.7.0: resolution: {integrity: sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==} @@ -7515,20 +8085,23 @@ packages: plimit-lit: 1.4.1 dev: true - /tsconfig-paths-jest/0.0.1: - resolution: {integrity: sha512-YKhUKqbteklNppC2NqL7dv1cWF8eEobgHVD5kjF1y9Q4ocqpBiaDlYslQ9eMhtbqIPRrA68RIEXqknEjlxdwxw==} - dev: true - /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib/2.4.0: - resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - /tslib/2.4.1: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + /tsutils/3.21.0_typescript@4.8.3: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.8.3 + dev: true + /tsutils/3.21.0_typescript@4.8.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -7550,7 +8123,7 @@ packages: smartwrap: 2.0.2 strip-ansi: 6.0.1 wcwidth: 1.0.1 - yargs: 17.6.0 + yargs: 17.6.2 dev: true /tunnel-agent/0.6.0: @@ -7583,7 +8156,7 @@ packages: /type-fest/0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} - dev: true + dev: false /type-fest/0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} @@ -7593,16 +8166,21 @@ packages: /type-fest/0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - dev: true /type-fest/0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} - dev: true /type-fest/0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} + + /typed-array-length/1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 dev: true /typed-rest-client/1.8.9: @@ -7631,6 +8209,27 @@ packages: hasBin: true dev: true + /typescript/4.9.4: + resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /typical/4.0.0: + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} + dev: true + + /typical/5.2.0: + resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==} + engines: {node: '>=8'} + dev: true + + /typical/7.1.1: + resolution: {integrity: sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==} + engines: {node: '>=12.17'} + dev: true + /uc.micro/1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} dev: true @@ -7653,14 +8252,14 @@ packages: engines: {node: '>=12.18'} dependencies: busboy: 1.6.0 - dev: true + dev: false /unique-string/2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} dependencies: crypto-random-string: 2.0.0 - dev: true + dev: false /universalify/0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -7670,6 +8269,10 @@ packages: /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + + /untildify/4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} dev: true /update-browserslist-db/1.0.10_browserslist@4.21.4: @@ -7686,13 +8289,13 @@ packages: /upper-case-first/2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /upper-case/2.0.2: resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /uri-js/4.4.1: @@ -7719,16 +8322,16 @@ packages: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true - dev: true + dev: false /uuid/8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + dev: false /uuid/9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} hasBin: true - dev: false /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -7748,7 +8351,6 @@ packages: dependencies: spdx-correct: 3.1.1 spdx-expression-parse: 3.0.1 - dev: true /vsce/2.13.0: resolution: {integrity: sha512-t1otQ2lqyi5Y/G6qUl9BEc561nEIYrZbLT8k+R1SoZaKNa6gaehaLGQG5zvB524YPGZOVvbOBzAXoO7Mor1J5g==} @@ -7810,6 +8412,10 @@ packages: /vscode-uri/3.0.6: resolution: {integrity: sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==} + dev: false + + /vscode-uri/3.0.7: + resolution: {integrity: sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==} /walker/1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -7824,14 +8430,12 @@ packages: /webidl-conversions/3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /whatwg-url/5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /when/3.7.8: resolution: {integrity: sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==} @@ -7859,6 +8463,18 @@ packages: path-exists: 4.0.0 dev: true + /which-typed-array/1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + /which/1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -7872,13 +8488,20 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: true + /wordwrapjs/4.0.1: + resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} + engines: {node: '>=8.0.0'} + dependencies: + reduce-flatten: 2.0.0 + typical: 5.2.0 + dev: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -7899,7 +8522,6 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /write-file-atomic/4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} @@ -7922,6 +8544,11 @@ packages: engines: {node: '>=4.0'} dev: true + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + /y18n/4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true @@ -7946,6 +8573,11 @@ packages: decamelize: 1.2.0 dev: true + /yargs-parser/20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -7968,6 +8600,19 @@ packages: yargs-parser: 18.1.3 dev: true + /yargs/16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + /yargs/17.5.1: resolution: {integrity: sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==} engines: {node: '>=12'} @@ -7994,6 +8639,19 @@ packages: yargs-parser: 21.1.1 dev: true + /yargs/17.6.2: + resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==} + engines: {node: '>=12'} + 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.8 + yargs-parser: 21.1.1 + dev: true + /yauzl/2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} dependencies: @@ -8015,7 +8673,6 @@ packages: /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true /zip-stream/4.1.0: resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==} @@ -8024,7 +8681,7 @@ packages: archiver-utils: 2.1.0 compress-commons: 4.1.1 readable-stream: 3.6.0 - dev: true + dev: false /zod-validation-error/0.2.1_zod@3.19.1: resolution: {integrity: sha512-zGg6P5EHi5V0dvyEeC8HBZd2pzp7QDKTngkSWgWunljrY+0SHkHyjI519D+u8/37BHkGHAFseWgnZ2Uq8LNFKg==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2d80ade63..928f8bfc9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,4 @@ packages: - 'packages/*' + - 'packages/plugins/*' - 'tests/*' diff --git a/samples/todo/.babelrc b/samples/todo/.babelrc deleted file mode 100644 index cfeaef173..000000000 --- a/samples/todo/.babelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "presets": ["next/babel"], - // "superjson-next" plugin uses superjson for serialization between getServerSideProps and client, - // so that types like Date and BigInt are properly handled - "plugins": ["superjson-next"] -} diff --git a/samples/todo/.env b/samples/todo/.env deleted file mode 100644 index 6ce1656c6..000000000 --- a/samples/todo/.env +++ /dev/null @@ -1,4 +0,0 @@ -NEXTAUTH_SECRET=abc123 -DATABASE_URL="postgresql://postgres:abc123@localhost:5432/todo?schema=public" -GITHUB_ID= -GITHUB_SECRET= diff --git a/samples/todo/.eslintrc.json b/samples/todo/.eslintrc.json deleted file mode 100644 index 758729586..000000000 --- a/samples/todo/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["next/core-web-vitals"] -} diff --git a/samples/todo/.vscode/launch.json b/samples/todo/.vscode/launch.json deleted file mode 100644 index e3d8e6e92..000000000 --- a/samples/todo/.vscode/launch.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Next.js: debug server-side", - "type": "node-terminal", - "request": "launch", - "command": "npm run dev" - } - ] -} diff --git a/samples/todo/README.md b/samples/todo/README.md deleted file mode 100644 index 9a1562589..000000000 --- a/samples/todo/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# A Collaborative Todo Sample - -This project is a collaborative todo app built with [Next.js](https://nextjs.org), [Next-Auth](nextauth.org), and [ZenStack](https://github.com/zenstackhq/zenstack). - -In this fictitious app, users can be invited to workspaces where they can collaborate on todos. Public todo lists are visible to all members in the workspace. - -See a live deployment at: https://zenstack-todo.vercel.app/. - -## Features: - -- User signup/signin -- Creating workspaces and inviting members -- Data isolation and permission control - -## Running the sample: - -1. Install dependencies - -```bash -npm install -``` - -2. Generate server and client-side code from model - -```bash -npm run generate -``` - -3. Synchronize database schma - -```bash -npm run db:push -``` - -4. Start dev server - -```bash -npm run dev -``` diff --git a/samples/todo/components/AuthGuard.tsx b/samples/todo/components/AuthGuard.tsx deleted file mode 100644 index 5111c7cad..000000000 --- a/samples/todo/components/AuthGuard.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useSession } from 'next-auth/react'; -import { useRouter } from 'next/router'; - -type Props = { - children: JSX.Element | JSX.Element[]; -}; - -export default function AuthGuard({ children }: Props) { - const { status } = useSession(); - const router = useRouter(); - - if (router.pathname === '/signup' || router.pathname === '/signin') { - return <>{children}; - } - - if (status === 'loading') { - return

Loading...

; - } else if (status === 'unauthenticated') { - router.push('/signin'); - return <>; - } else { - return <>{children}; - } -} diff --git a/samples/todo/components/Avatar.tsx b/samples/todo/components/Avatar.tsx deleted file mode 100644 index ae8626957..000000000 --- a/samples/todo/components/Avatar.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { UserIcon } from '@heroicons/react/24/outline'; -import { User } from 'next-auth'; -import Image from 'next/image'; - -type Props = { - user: User; - size?: number; -}; - -export default function Avatar({ user, size }: Props) { - return ( -
- avatar -
- ); -} diff --git a/samples/todo/components/BreadCrumb.tsx b/samples/todo/components/BreadCrumb.tsx deleted file mode 100644 index f537f9c1b..000000000 --- a/samples/todo/components/BreadCrumb.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { List, Space } from '@zenstackhq/runtime/types'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; - -type Props = { - space: Space; - list?: List; -}; - -export default function BreadCrumb({ space, list }: Props) { - const router = useRouter(); - - const parts = router.asPath.split('/').filter((p) => p); - const [base] = parts; - if (base !== 'space') { - return <>; - } - - const items: Array<{ text: string; link: string }> = []; - - items.push({ text: 'Home', link: '/' }); - items.push({ text: space.name || '', link: `/space/${space.slug}` }); - - if (list) { - items.push({ - text: list?.title || '', - link: `/space/${space.slug}/${list.id}`, - }); - } - - return ( -
-
    - {items.map((item, i) => ( -
  • - - {item.text} - -
  • - ))} -
-
- ); -} diff --git a/samples/todo/components/ManageMembers.tsx b/samples/todo/components/ManageMembers.tsx deleted file mode 100644 index f72bfdad2..000000000 --- a/samples/todo/components/ManageMembers.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; -import { useCurrentUser } from '@lib/context'; -import { Space, SpaceUserRole } from '@zenstackhq/runtime/types'; -import { HooksError, ServerErrorCode } from '@zenstackhq/runtime/client'; -import { ChangeEvent, KeyboardEvent, useState } from 'react'; -import { toast } from 'react-toastify'; -import Avatar from './Avatar'; -import { useSpaceUser } from '@zenstackhq/runtime/client'; - -type Props = { - space: Space; -}; - -export default function ManageMembers({ space }: Props) { - const [email, setEmail] = useState(''); - const [role, setRole] = useState(SpaceUserRole.USER); - const user = useCurrentUser(); - - const { find, create: addMember, del: delMember } = useSpaceUser(); - const { data: members } = find({ - where: { - spaceId: space.id, - }, - include: { - user: true, - }, - orderBy: { - role: 'desc', - }, - }); - - const inviteUser = async () => { - try { - const r = await addMember({ - data: { - user: { - connect: { - email, - }, - }, - space: { - connect: { - id: space.id, - }, - }, - role, - }, - }); - console.log('SpaceUser created:', r); - } catch (err: any) { - console.error(JSON.stringify(err)); - if (err.info?.code) { - const { info } = err as HooksError; - if (info.code === ServerErrorCode.UNIQUE_CONSTRAINT_VIOLATION) { - toast.error('User is already a member of the space'); - } else if ( - info.code === ServerErrorCode.REFERENCE_CONSTRAINT_VIOLATION - ) { - toast.error('User is not found for this email'); - } - } else { - toast.error(`Error occurred: ${err}`); - } - } - }; - - const removeMember = async (id: string) => { - if (confirm(`Are you sure to remove this member from space?`)) { - await delMember(id); - } - }; - - return ( -
-
- ) => { - setEmail(e.currentTarget.value); - }} - onKeyUp={(e: KeyboardEvent) => { - if (e.key === 'Enter') { - inviteUser(); - } - }} - /> - - - - -
- -
    - {members?.map((member) => ( -
  • -
    -
    - -
    -

    - {member.user.name || member.user.email} -

    -

    {member.role}

    -
    -
    - {user?.id !== member.user.id && ( - { - removeMember(member.id); - }} - /> - )} -
    -
  • - ))} -
-
- ); -} diff --git a/samples/todo/components/NavBar.tsx b/samples/todo/components/NavBar.tsx deleted file mode 100644 index 6266c6c31..000000000 --- a/samples/todo/components/NavBar.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import Image from 'next/image'; -import Avatar from './Avatar'; -import { signOut } from 'next-auth/react'; -import Link from 'next/link'; -import { Space } from '@zenstackhq/runtime/types'; -import { User } from 'next-auth'; - -type Props = { - space: Space | undefined; - user: User | undefined; -}; - -export default function NavBar({ user, space }: Props) { - const onSignout = async () => { - await signOut({ callbackUrl: '/signin' }); - }; - - return ( -
- -
-
- -
    -
  • - {user &&
    {user.name || user.email}
    } -
  • -
  • - Logout -
  • -
-
-
-
- ); -} diff --git a/samples/todo/components/SpaceMembers.tsx b/samples/todo/components/SpaceMembers.tsx deleted file mode 100644 index 419692a91..000000000 --- a/samples/todo/components/SpaceMembers.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useSpaceUser } from '@zenstackhq/runtime/client'; -import { useCurrentSpace } from '@lib/context'; -import { PlusIcon } from '@heroicons/react/24/outline'; -import Avatar from './Avatar'; -import ManageMembers from './ManageMembers'; -import { Space } from '@zenstackhq/runtime/types'; - -function ManagementDialog(space?: Space) { - if (!space) return undefined; - return ( - <> - - - -
-
-

- Manage Members of {space.name} -

- -
- -
- -
- -
-
-
- - ); -} - -export default function SpaceMembers() { - const space = useCurrentSpace(); - - const { find: findMembers } = useSpaceUser(); - const { data: members } = findMembers( - { - where: { - spaceId: space?.id, - }, - include: { - user: true, - }, - orderBy: { - role: 'desc', - }, - }, - { disabled: !space } - ); - - return ( -
- {ManagementDialog(space)} - {members && ( - - )} -
- ); -} diff --git a/samples/todo/components/Spaces.tsx b/samples/todo/components/Spaces.tsx deleted file mode 100644 index eaac0f578..000000000 --- a/samples/todo/components/Spaces.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Space } from '@zenstackhq/runtime/types'; -import Link from 'next/link'; - -type Props = { - spaces: Space[]; -}; - -export default function Spaces({ spaces }: Props) { - return ( - - ); -} diff --git a/samples/todo/components/TimeInfo.tsx b/samples/todo/components/TimeInfo.tsx deleted file mode 100644 index 0d03e9587..000000000 --- a/samples/todo/components/TimeInfo.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import moment from 'moment'; - -type Props = { - value: { createdAt: Date; updatedAt: Date; completedAt?: Date | null }; -}; - -export default function TimeInfo({ value }: Props) { - return ( -

- {value.completedAt - ? `Completed ${moment(value.completedAt).fromNow()}` - : value.createdAt === value.updatedAt - ? `Created ${moment(value.createdAt).fromNow()}` - : `Updated ${moment(value.updatedAt).fromNow()}`} -

- ); -} diff --git a/samples/todo/components/Todo.tsx b/samples/todo/components/Todo.tsx deleted file mode 100644 index 2739106e6..000000000 --- a/samples/todo/components/Todo.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { TrashIcon } from '@heroicons/react/24/outline'; -import { useTodo } from '@zenstackhq/runtime/client'; -import { Todo, User } from '@zenstackhq/runtime/types'; -import { ChangeEvent, useEffect, useState } from 'react'; -import Avatar from './Avatar'; -import TimeInfo from './TimeInfo'; - -type Props = { - value: Todo & { owner: User }; - updated?: (value: Todo) => any; - deleted?: (value: Todo) => any; -}; - -export default function Component({ value, updated, deleted }: Props) { - const [completed, setCompleted] = useState(!!value.completedAt); - const { update, del } = useTodo(); - - useEffect(() => { - if (!!value.completedAt !== completed) { - update(value.id, { - data: { completedAt: completed ? new Date() : null }, - }).then((newValue) => { - if (updated && newValue) { - updated(newValue); - } - }); - } - }); - - const deleteTodo = async () => { - await del(value.id); - if (deleted) { - deleted(value); - } - }; - - return ( -
-
-

- {value.title} -

-
- ) => - setCompleted(e.currentTarget.checked) - } - /> - { - deleteTodo(); - }} - /> -
-
-
- - -
-
- ); -} diff --git a/samples/todo/components/TodoList.tsx b/samples/todo/components/TodoList.tsx deleted file mode 100644 index 959a2c811..000000000 --- a/samples/todo/components/TodoList.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import Image from 'next/image'; -import { List } from '@zenstackhq/runtime/types'; -import { customAlphabet } from 'nanoid'; -import { LockClosedIcon, TrashIcon } from '@heroicons/react/24/outline'; -import { User } from 'next-auth'; -import Avatar from './Avatar'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; -import { useList } from '@zenstackhq/runtime/client'; -import TimeInfo from './TimeInfo'; - -type Props = { - value: List & { owner: User }; - deleted?: (value: List) => void; -}; - -export default function TodoList({ value, deleted }: Props) { - const router = useRouter(); - - const { del } = useList(); - - const deleteList = async () => { - if (confirm('Are you sure to delete this list?')) { - await del(value.id); - if (deleted) { - deleted(value); - } - } - }; - - return ( -
- - -
- Cover -
-
- -
- - -

- {value.title || 'Missing Title'} -

-
- -
-
- -
-
- - {value.private && ( -
- -
- )} - { - deleteList(); - }} - /> -
-
-
-
- ); -} diff --git a/samples/todo/components/WithNavBar.tsx b/samples/todo/components/WithNavBar.tsx deleted file mode 100644 index 056ce5ae4..000000000 --- a/samples/todo/components/WithNavBar.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useCurrentSpace, useCurrentUser } from '@lib/context'; -import NavBar from './NavBar'; - -type Props = { - children: JSX.Element | JSX.Element[] | undefined; -}; - -export default function WithNavBar({ children }: Props) { - const user = useCurrentUser(); - const space = useCurrentSpace(); - - return ( - <> - - {children} - - ); -} diff --git a/samples/todo/lib/context.ts b/samples/todo/lib/context.ts deleted file mode 100644 index 5793f1d43..000000000 --- a/samples/todo/lib/context.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useSpace } from '@zenstackhq/runtime/client'; -import { Space } from '@zenstackhq/runtime/types'; -import { User } from 'next-auth'; -import { useSession } from 'next-auth/react'; -import { useRouter } from 'next/router'; -import { createContext } from 'react'; - -export const UserContext = createContext(undefined); - -export function useCurrentUser() { - const { data: session } = useSession(); - return session?.user; -} - -export const SpaceContext = createContext(undefined); - -export function useCurrentSpace() { - const router = useRouter(); - const { find } = useSpace(); - const spaces = find( - { - where: { - slug: router.query.slug as string, - }, - }, - { - disabled: !router.query.slug, - } - ); - - return spaces.data?.[0]; -} diff --git a/samples/todo/lib/query-utils.ts b/samples/todo/lib/query-utils.ts deleted file mode 100644 index 572d8eb85..000000000 --- a/samples/todo/lib/query-utils.ts +++ /dev/null @@ -1,11 +0,0 @@ -import service, { QueryContext } from '@zenstackhq/runtime/server'; - -export async function getSpaceBySlug(queryContext: QueryContext, slug: string) { - const spaces = await service.space.find(queryContext, { - where: { slug }, - }); - if (spaces.length === 0) { - throw new Error('Space not found: ' + slug); - } - return spaces[0]; -} diff --git a/samples/todo/next.config.js b/samples/todo/next.config.js deleted file mode 100644 index ccd6c89af..000000000 --- a/samples/todo/next.config.js +++ /dev/null @@ -1,14 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - swcMinify: true, - images: { - domains: [ - 'picsum.photos', - 'lh3.googleusercontent.com', - 'avatars.githubusercontent.com', - ], - }, -}; - -module.exports = nextConfig; diff --git a/samples/todo/package-lock.json b/samples/todo/package-lock.json deleted file mode 100644 index 5e79d9d8d..000000000 --- a/samples/todo/package-lock.json +++ /dev/null @@ -1,8720 +0,0 @@ -{ - "name": "todo", - "version": "0.5.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "todo", - "version": "0.5.0", - "dependencies": { - "@heroicons/react": "^2.0.12", - "@prisma/client": "^4.7.0", - "@zenstackhq/next-auth": "^0.5.0", - "@zenstackhq/runtime": "^0.5.0", - "babel-plugin-superjson-next": "^0.4.5", - "bcryptjs": "^2.4.3", - "daisyui": "^2.31.0", - "moment": "^2.29.4", - "nanoid": "^4.0.0", - "next": "12.3.1", - "next-auth": "^4.15.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-toastify": "^9.0.8" - }, - "devDependencies": { - "@tailwindcss/line-clamp": "^0.4.2", - "@types/bcryptjs": "^2.4.2", - "@types/node": "^14.17.3", - "@types/react": "18.0.21", - "@types/react-dom": "18.0.6", - "autoprefixer": "^10.4.12", - "eslint": "^7.19.0", - "eslint-config-next": "12.3.1", - "postcss": "^8.4.16", - "tailwindcss": "^3.1.8", - "typescript": "^4.6.2", - "zenstack": "^0.5.0" - } - }, - "../../packages/runtime": { - "name": "@zenstackhq/runtime", - "version": "0.3.10", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@zenstackhq/internal": "latest", - "colors": "1.4.0", - "cuid": "^2.1.8", - "decimal.js": "^10.4.2", - "deepcopy": "^2.1.0", - "swr": "^1.3.0", - "zod": "^3.19.1", - "zod-validation-error": "^0.2.1" - }, - "devDependencies": { - "@types/bcryptjs": "^2.4.2", - "@types/jest": "^29.0.3", - "@types/node": "^14.18.29", - "rimraf": "^3.0.2", - "typescript": "^4.9.3" - }, - "peerDependencies": { - "@types/bcryptjs": "^2.4.2", - "bcryptjs": "^2.4.3", - "next": "^12.3.1", - "react": "^17.0.2 || ^18", - "react-dom": "^17.0.2 || ^18" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.6.tgz", - "integrity": "sha512-oWNn1ZlGde7b4i/3tnixpH9qI0bOAACiUs+KEES4UUCnsPjVWFlWdLV/iwJuPC2qp3EowbAqsm+0XqNwnwYhxA==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@chevrotain/types": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-9.1.0.tgz", - "integrity": "sha512-3hbCD1CThkv9gnaSIPq0GUXwKni68e0ph6jIHwCvcWiQ4JB2xi8bFxBain0RF04qHUWuDjgnZLj4rLgimuGO+g==", - "dev": true - }, - "node_modules/@chevrotain/utils": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-9.1.0.tgz", - "integrity": "sha512-llLJZ8OAlZrjGlBvamm6Zdo/HmGAcCLq5gx7cSwUX8No+n/8ip+oaC4x33IdZIif8+Rh5dQUIZXmfbSghiOmNQ==", - "dev": true - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@heroicons/react": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.12.tgz", - "integrity": "sha512-FZxKh3i9aKIDxyALTgIpSF2t6V6/eZfF5mRu41QlwkX3Oxzecdm1u6dpft6PQGxIBwO7TKYWaMAYYL8mp/EaOg==", - "peerDependencies": { - "react": ">= 16" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@next-auth/prisma-adapter": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", - "integrity": "sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==", - "peerDependencies": { - "@prisma/client": ">=2.26.0 || >=3", - "next-auth": "^4" - } - }, - "node_modules/@next/env": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", - "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz", - "integrity": "sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==", - "dev": true, - "dependencies": { - "glob": "7.1.7" - } - }, - "node_modules/@next/swc-android-arm-eabi": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", - "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", - "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", - "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", - "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-freebsd-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", - "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", - "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", - "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", - "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", - "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", - "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", - "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", - "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", - "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@panva/hkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", - "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/@prisma/client": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.0.tgz", - "integrity": "sha512-keXMa0oJWJGOzMEFKp+CEgzJPwnOtGSrnTWw6qMYxnypYrRFdNxqyA06EzELZexBhgM4oLooZ1jDJ3iy46wExA==", - "hasInstallScript": true, - "dependencies": { - "@prisma/engines-version": "4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "prisma": "*" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - } - } - }, - "node_modules/@prisma/engines": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.0.tgz", - "integrity": "sha512-afKrVFktaZ1pOK12/uFl2hRsBWIJZuC5FdDtacuKk5x/mR+rC5AbA+PlN3ZCZbmYTaeiBMHjcU5wbT5z2N3nSQ==", - "devOptional": true, - "hasInstallScript": true - }, - "node_modules/@prisma/engines-version": { - "version": "4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635.tgz", - "integrity": "sha512-ImczGEQ8NS1OUApEeyAGxC4uLTtQp0wI1+2wM4MeQLVwIQbyMHk1vOhWWE8Pwbi3rnzLcPvsIrd9sm6oNXhERw==" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", - "dev": true - }, - "node_modules/@swc/helpers": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", - "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/line-clamp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz", - "integrity": "sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==", - "dev": true, - "peerDependencies": { - "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.17.0.tgz", - "integrity": "sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/bcryptjs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", - "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "14.18.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz", - "integrity": "sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.0.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.21.tgz", - "integrity": "sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", - "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.40.1.tgz", - "integrity": "sha512-IK6x55va5w4YvXd4b3VrXQPldV9vQTxi5ov+g4pMANsXPTXOcfjx08CRR1Dfrcc51syPtXHF5bgLlMHYFrvQtg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.40.1.tgz", - "integrity": "sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.40.1.tgz", - "integrity": "sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz", - "integrity": "sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz", - "integrity": "sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.40.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@zenstackhq/next-auth": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@zenstackhq/next-auth/-/next-auth-0.5.0.tgz", - "integrity": "sha512-/6kMkoqea2WWz9oAz9gYBRfJdNKjCmrYQ79kTsLWSRPP2E89GsVTSCRZPU7qnGzBL9B3kVdYWA/yHBuTwpYqbA==", - "dependencies": { - "@next-auth/prisma-adapter": "^1.0.5", - "@zenstackhq/runtime": "0.5.0", - "bcryptjs": "^2.4.3" - }, - "peerDependencies": { - "next-auth": "^4.0.0" - } - }, - "node_modules/@zenstackhq/runtime": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.5.0.tgz", - "integrity": "sha512-eneaJ1+xThyg9dnIvOvcm3buIVSX9XU8biGE13wBGAkgDWxuOWLNITNEF4MrV4WGwQjwKAR8o0WZaGTMOrMHfw==", - "dependencies": { - "@types/bcryptjs": "^2.4.2", - "bcryptjs": "^2.4.3", - "colors": "1.4.0", - "cuid": "^2.1.8", - "decimal.js": "^10.4.2", - "deepcopy": "^2.1.0", - "superjson": "^1.11.0", - "swr": "^1.3.0", - "tslib": "^2.4.1", - "zod": "^3.19.1", - "zod-validation-error": "^0.2.1" - }, - "peerDependencies": { - "next": "^12.3.1 || ^13", - "react": "^17.0.2 || ^18", - "react-dom": "^17.0.2 || ^18" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/array-includes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", - "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.12", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", - "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001407", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "node_modules/babel-plugin-superjson-next": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-superjson-next/-/babel-plugin-superjson-next-0.4.5.tgz", - "integrity": "sha512-k7S99Qpsbi3OSdlCMXEiklzxepM6QbYEIUsrjgSkpx+ksT0iNfdY2r1kCzBK2UjG8fLN6NZEKpDA8XpG2pbDSA==", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/types": "^7.13.17", - "hoist-non-react-statics": "^3.3.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "next": ">=9.0.0", - "superjson": "1.x" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001422", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001422.tgz", - "integrity": "sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "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/change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/chevrotain": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-9.1.0.tgz", - "integrity": "sha512-A86/55so63HCfu0dgGg3j9u8uuuBOrSqly1OhBZxRu2x6sAKILLzfVjbGMw45kgier6lz45EzcjjWtTRgoT84Q==", - "dev": true, - "dependencies": { - "@chevrotain/types": "^9.1.0", - "@chevrotain/utils": "^9.1.0", - "regexp-to-ast": "0.5.0" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "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" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/code-block-writer": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", - "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", - "dev": true - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/copy-anything": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", - "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", - "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", - "dev": true - }, - "node_modules/cuid": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz", - "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==" - }, - "node_modules/daisyui": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.31.0.tgz", - "integrity": "sha512-qepRXgQPLNcJ8ZPZy+dUvsC7mRWvMLRcVMe85/wZA60Tnhm/bkidhOzdllL8aAk2JX+W/xlIsTJ8NZFpPm+eyw==", - "dependencies": { - "color": "^4.2", - "css-selector-tokenizer": "^0.8.0", - "postcss-js": "^4.0.0", - "tailwindcss": "^3" - }, - "peerDependencies": { - "autoprefixer": "^10.0.2", - "postcss": "^8.1.6" - } - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepcopy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/deepcopy/-/deepcopy-2.1.0.tgz", - "integrity": "sha512-8cZeTb1ZKC3bdSCP6XOM1IsTczIO73fdqtwa2B0N15eAz7gmyhQo+mc5gnFuulsgN3vIQYmTgbmQVKalH1dKvQ==", - "dependencies": { - "type-detect": "^4.0.8" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.3.1.tgz", - "integrity": "sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg==", - "dev": true, - "dependencies": { - "@next/eslint-plugin-next": "12.3.1", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.21.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^2.7.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", - "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "glob": "^7.2.0", - "is-glob": "^4.0.3", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "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/globby/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, - "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-what": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", - "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jose": { - "version": "4.10.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.10.4.tgz", - "integrity": "sha512-eBH77Xs9Yc/oTDvukhAEDVMijhekPuNktXJL4tUlB22jqKP1k48v5nmsUmc8feoJPsxB3HsfEt2LbVSoz+1mng==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/langium": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/langium/-/langium-0.5.0.tgz", - "integrity": "sha512-7mheQQtyyPm8r9R+8sPZopcdzEcHtCC+M52m8ZbotzNql8RPLs3+QbDY6EZ98ejKtJCGt9RviTkfZEt1LxX86w==", - "dev": true, - "dependencies": { - "chevrotain": "^9.1.0", - "vscode-languageserver": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.7", - "vscode-uri": "^3.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dev": true, - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mixpanel": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.17.0.tgz", - "integrity": "sha512-DY5WeOy/hmkPrNiiZugJpWR0iMuOwuj1a3u0bgwB2eUFRV6oIew/pIahhpawdbNjb+Bye4a8ID3gefeNPvL81g==", - "dev": true, - "dependencies": { - "https-proxy-agent": "5.0.0" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz", - "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^14 || ^16 || >=18" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", - "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", - "dependencies": { - "@next/env": "12.3.1", - "@swc/helpers": "0.4.11", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.0.7", - "use-sync-external-store": "1.2.0" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=12.22.0" - }, - "optionalDependencies": { - "@next/swc-android-arm-eabi": "12.3.1", - "@next/swc-android-arm64": "12.3.1", - "@next/swc-darwin-arm64": "12.3.1", - "@next/swc-darwin-x64": "12.3.1", - "@next/swc-freebsd-x64": "12.3.1", - "@next/swc-linux-arm-gnueabihf": "12.3.1", - "@next/swc-linux-arm64-gnu": "12.3.1", - "@next/swc-linux-arm64-musl": "12.3.1", - "@next/swc-linux-x64-gnu": "12.3.1", - "@next/swc-linux-x64-musl": "12.3.1", - "@next/swc-win32-arm64-msvc": "12.3.1", - "@next/swc-win32-ia32-msvc": "12.3.1", - "@next/swc-win32-x64-msvc": "12.3.1" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^6.0.0 || ^7.0.0", - "react": "^17.0.2 || ^18.0.0-0", - "react-dom": "^17.0.2 || ^18.0.0-0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-auth": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.15.1.tgz", - "integrity": "sha512-yB/cSEqslaXAsLZ3lvkJbB7bRR5JeZvfUXSw0CmWeP+TaMZWB85fG9PWdfBeFrDCLBsoyATw9FF6fzApE0SxSw==", - "dependencies": { - "@babel/runtime": "^7.16.3", - "@panva/hkdf": "^1.0.1", - "cookie": "^0.5.0", - "jose": "^4.9.3", - "oauth": "^0.9.15", - "openid-client": "^5.1.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" - }, - "engines": { - "node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0" - }, - "peerDependencies": { - "next": "^12.2.5 || ^13", - "nodemailer": "^6.6.5", - "react": "^17.0.2 || ^18", - "react-dom": "^17.0.2 || ^18" - }, - "peerDependenciesMeta": { - "nodemailer": { - "optional": true - } - } - }, - "node_modules/next/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/oidc-token-hash": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", - "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openid-client": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.2.1.tgz", - "integrity": "sha512-KPxqWnxobG/70Cxqyvd43RWfCfHedFnCdHSBpw5f7WnTnuBAeBnvot/BIo+brrcTr0wyAYUlL/qejQSGwWtdIg==", - "dependencies": { - "jose": "^4.10.0", - "lru-cache": "^6.0.0", - "object-hash": "^2.0.1", - "oidc-token-hash": "^5.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "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.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", - "dev": true, - "dependencies": { - "bl": "^5.0.0", - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.4.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", - "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/preact": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.2.tgz", - "integrity": "sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", - "dependencies": { - "pretty-format": "^3.8.0" - }, - "peerDependencies": { - "preact": ">=10" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" - }, - "node_modules/prisma": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.0.tgz", - "integrity": "sha512-VsecNo0Ca3+bDTzSpJqIpdupKVhhQ8aOYeWc09JlUM89knqvhSrlMrg0U8BiOD4tFrY1OPaCcraK8leDBxKMBg==", - "devOptional": true, - "hasInstallScript": true, - "dependencies": { - "@prisma/engines": "4.7.0" - }, - "bin": { - "prisma": "build/index.js", - "prisma2": "build/index.js" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promisify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/promisify/-/promisify-0.0.3.tgz", - "integrity": "sha512-CcBGsRhhq466fsZVyHfptuKqon6eih0CqMsJE0kWIIjbpVNEyDoaKLELm2WVs//W/WXRBHip+6xhTExTkHUwtA==", - "dev": true, - "dependencies": { - "when": "" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-toastify": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.8.tgz", - "integrity": "sha512-EwM+teWt49HSHx+67qI08yLAW1zAsBxCXLCsUfxHYv1W7/R3ZLhrqKalh7j+kjgPna1h5LQMSMwns4tB4ww2yQ==", - "dependencies": { - "clsx": "^1.1.1" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" - }, - "node_modules/regexp-to-ast": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", - "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sleep-promise": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", - "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", - "dev": true - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "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/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/styled-jsx": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/superjson": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.11.0.tgz", - "integrity": "sha512-6PfAg1FKhqkwWvPb2uXhH4MkMttdc17eJ91+Aoz4s1XUEDZFmLfFx/xVA3wgkPxAGy5dpozgGdK6V/n20Wj9yg==", - "dependencies": { - "copy-anything": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swr": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz", - "integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==", - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tailwindcss": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.0.tgz", - "integrity": "sha512-ARh/W0uH5UlWIC2nn02V0+5fyF0k6qZliyt4QYic2upOhPUE/Spu1EURNc9txJ3+4j8OEmdigqfDpw4d2tA4vA==", - "dependencies": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.17", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-morph": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-16.0.0.tgz", - "integrity": "sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==", - "dev": true, - "dependencies": { - "@ts-morph/common": "~0.17.0", - "code-block-writer": "^11.0.3" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageclient": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", - "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4", - "semver": "^7.3.5", - "vscode-languageserver-protocol": "3.17.2" - }, - "engines": { - "vscode": "^1.67.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", - "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", - "dev": true, - "dependencies": { - "vscode-languageserver-protocol": "3.17.2" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", - "dev": true, - "dependencies": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", - "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==", - "dev": true - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz", - "integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==", - "dev": true - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/zenstack": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.5.0.tgz", - "integrity": "sha512-nMYl5b9/9FuKBQP++xnJoeM/9bySavgfeRtT552BP7S8I9f8iRK/SxMJqaD8IS5mdFhXj3sdQULjd43euLs8wA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@zenstackhq/runtime": "0.5.0", - "async-exit-hook": "^2.0.1", - "change-case": "^4.1.2", - "chevrotain": "^9.1.0", - "colors": "1.4.0", - "commander": "^8.3.0", - "cuid": "^2.1.8", - "langium": "^0.5.0", - "mixpanel": "^0.17.0", - "node-machine-id": "^1.1.12", - "ora": "^6.1.2", - "pluralize": "^8.0.0", - "prisma": "~4.7.0", - "promisify": "^0.0.3", - "sleep-promise": "^9.1.0", - "ts-morph": "^16.0.0", - "uuid": "^9.0.0", - "vscode-jsonrpc": "^8.0.2", - "vscode-languageclient": "^8.0.2", - "vscode-languageserver": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.7", - "vscode-uri": "^3.0.6" - }, - "bin": { - "zenstack": "bin/cli" - }, - "engines": { - "vscode": "^1.56.0" - } - }, - "node_modules/zenstack/node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/zod": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz", - "integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-0.2.1.tgz", - "integrity": "sha512-zGg6P5EHi5V0dvyEeC8HBZd2pzp7QDKTngkSWgWunljrY+0SHkHyjI519D+u8/37BHkGHAFseWgnZ2Uq8LNFKg==", - "dependencies": { - "@swc/helpers": "^0.4.11" - }, - "engines": { - "node": "^14.17 || >=16.0.0" - }, - "peerDependencies": { - "zod": "^3.18.0" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/runtime-corejs3": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.6.tgz", - "integrity": "sha512-oWNn1ZlGde7b4i/3tnixpH9qI0bOAACiUs+KEES4UUCnsPjVWFlWdLV/iwJuPC2qp3EowbAqsm+0XqNwnwYhxA==", - "dev": true, - "requires": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@chevrotain/types": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-9.1.0.tgz", - "integrity": "sha512-3hbCD1CThkv9gnaSIPq0GUXwKni68e0ph6jIHwCvcWiQ4JB2xi8bFxBain0RF04qHUWuDjgnZLj4rLgimuGO+g==", - "dev": true - }, - "@chevrotain/utils": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-9.1.0.tgz", - "integrity": "sha512-llLJZ8OAlZrjGlBvamm6Zdo/HmGAcCLq5gx7cSwUX8No+n/8ip+oaC4x33IdZIif8+Rh5dQUIZXmfbSghiOmNQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, - "@heroicons/react": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.12.tgz", - "integrity": "sha512-FZxKh3i9aKIDxyALTgIpSF2t6V6/eZfF5mRu41QlwkX3Oxzecdm1u6dpft6PQGxIBwO7TKYWaMAYYL8mp/EaOg==", - "requires": {} - }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@next-auth/prisma-adapter": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", - "integrity": "sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==", - "requires": {} - }, - "@next/env": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", - "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" - }, - "@next/eslint-plugin-next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz", - "integrity": "sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==", - "dev": true, - "requires": { - "glob": "7.1.7" - } - }, - "@next/swc-android-arm-eabi": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", - "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", - "optional": true - }, - "@next/swc-android-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", - "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", - "optional": true - }, - "@next/swc-darwin-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", - "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", - "optional": true - }, - "@next/swc-darwin-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", - "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", - "optional": true - }, - "@next/swc-freebsd-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", - "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", - "optional": true - }, - "@next/swc-linux-arm-gnueabihf": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", - "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", - "optional": true - }, - "@next/swc-linux-arm64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", - "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", - "optional": true - }, - "@next/swc-linux-arm64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", - "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", - "optional": true - }, - "@next/swc-linux-x64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", - "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", - "optional": true - }, - "@next/swc-linux-x64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", - "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", - "optional": true - }, - "@next/swc-win32-arm64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", - "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", - "optional": true - }, - "@next/swc-win32-ia32-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", - "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", - "optional": true - }, - "@next/swc-win32-x64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", - "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", - "optional": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@panva/hkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", - "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==" - }, - "@prisma/client": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.0.tgz", - "integrity": "sha512-keXMa0oJWJGOzMEFKp+CEgzJPwnOtGSrnTWw6qMYxnypYrRFdNxqyA06EzELZexBhgM4oLooZ1jDJ3iy46wExA==", - "requires": { - "@prisma/engines-version": "4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635" - } - }, - "@prisma/engines": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.0.tgz", - "integrity": "sha512-afKrVFktaZ1pOK12/uFl2hRsBWIJZuC5FdDtacuKk5x/mR+rC5AbA+PlN3ZCZbmYTaeiBMHjcU5wbT5z2N3nSQ==", - "devOptional": true - }, - "@prisma/engines-version": { - "version": "4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.0-74.39190b250ebc338586e25e6da45e5e783bc8a635.tgz", - "integrity": "sha512-ImczGEQ8NS1OUApEeyAGxC4uLTtQp0wI1+2wM4MeQLVwIQbyMHk1vOhWWE8Pwbi3rnzLcPvsIrd9sm6oNXhERw==" - }, - "@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", - "dev": true - }, - "@swc/helpers": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", - "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", - "requires": { - "tslib": "^2.4.0" - } - }, - "@tailwindcss/line-clamp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz", - "integrity": "sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==", - "dev": true, - "requires": {} - }, - "@ts-morph/common": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.17.0.tgz", - "integrity": "sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==", - "dev": true, - "requires": { - "fast-glob": "^3.2.11", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "@types/bcryptjs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", - "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==" - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/node": { - "version": "14.18.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz", - "integrity": "sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "@types/react": { - "version": "18.0.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.21.tgz", - "integrity": "sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", - "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "@typescript-eslint/parser": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.40.1.tgz", - "integrity": "sha512-IK6x55va5w4YvXd4b3VrXQPldV9vQTxi5ov+g4pMANsXPTXOcfjx08CRR1Dfrcc51syPtXHF5bgLlMHYFrvQtg==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.40.1.tgz", - "integrity": "sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1" - } - }, - "@typescript-eslint/types": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.40.1.tgz", - "integrity": "sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz", - "integrity": "sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz", - "integrity": "sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.40.1", - "eslint-visitor-keys": "^3.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } - } - }, - "@zenstackhq/next-auth": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@zenstackhq/next-auth/-/next-auth-0.5.0.tgz", - "integrity": "sha512-/6kMkoqea2WWz9oAz9gYBRfJdNKjCmrYQ79kTsLWSRPP2E89GsVTSCRZPU7qnGzBL9B3kVdYWA/yHBuTwpYqbA==", - "requires": { - "@next-auth/prisma-adapter": "^1.0.5", - "@zenstackhq/runtime": "0.5.0", - "bcryptjs": "^2.4.3" - } - }, - "@zenstackhq/runtime": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.5.0.tgz", - "integrity": "sha512-eneaJ1+xThyg9dnIvOvcm3buIVSX9XU8biGE13wBGAkgDWxuOWLNITNEF4MrV4WGwQjwKAR8o0WZaGTMOrMHfw==", - "requires": { - "@types/bcryptjs": "^2.4.2", - "bcryptjs": "^2.4.3", - "colors": "1.4.0", - "cuid": "^2.1.8", - "decimal.js": "^10.4.2", - "deepcopy": "^2.1.0", - "superjson": "^1.11.0", - "swr": "^1.3.0", - "tslib": "^2.4.1", - "zod": "^3.19.1", - "zod-validation-error": "^0.2.1" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "array-includes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", - "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - } - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.12", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", - "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", - "requires": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001407", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", - "dev": true - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "babel-plugin-superjson-next": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-superjson-next/-/babel-plugin-superjson-next-0.4.5.tgz", - "integrity": "sha512-k7S99Qpsbi3OSdlCMXEiklzxepM6QbYEIUsrjgSkpx+ksT0iNfdY2r1kCzBK2UjG8fLN6NZEKpDA8XpG2pbDSA==", - "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/types": "^7.13.17", - "hoist-non-react-statics": "^3.3.2" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "requires": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" - }, - "caniuse-lite": { - "version": "1.0.30001422", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001422.tgz", - "integrity": "sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==" - }, - "capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, - "requires": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "chevrotain": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-9.1.0.tgz", - "integrity": "sha512-A86/55so63HCfu0dgGg3j9u8uuuBOrSqly1OhBZxRu2x6sAKILLzfVjbGMw45kgier6lz45EzcjjWtTRgoT84Q==", - "dev": true, - "requires": { - "@chevrotain/types": "^9.1.0", - "@chevrotain/utils": "^9.1.0", - "regexp-to-ast": "0.5.0" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.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" - } - }, - "cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "requires": { - "restore-cursor": "^4.0.0" - } - }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "code-block-writer": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", - "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", - "dev": true - }, - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "copy-anything": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", - "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", - "requires": { - "is-what": "^4.1.8" - } - }, - "core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", - "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", - "dev": true - }, - "cuid": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz", - "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==" - }, - "daisyui": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.31.0.tgz", - "integrity": "sha512-qepRXgQPLNcJ8ZPZy+dUvsC7mRWvMLRcVMe85/wZA60Tnhm/bkidhOzdllL8aAk2JX+W/xlIsTJ8NZFpPm+eyw==", - "requires": { - "color": "^4.2", - "css-selector-tokenizer": "^0.8.0", - "postcss-js": "^4.0.0", - "tailwindcss": "^3" - } - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepcopy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/deepcopy/-/deepcopy-2.1.0.tgz", - "integrity": "sha512-8cZeTb1ZKC3bdSCP6XOM1IsTczIO73fdqtwa2B0N15eAz7gmyhQo+mc5gnFuulsgN3vIQYmTgbmQVKalH1dKvQ==", - "requires": { - "type-detect": "^4.0.8" - } - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" - }, - "detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "requires": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - } - }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-config-next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.3.1.tgz", - "integrity": "sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg==", - "dev": true, - "requires": { - "@next/eslint-plugin-next": "12.3.1", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.21.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^2.7.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-import-resolver-typescript": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", - "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "glob": "^7.2.0", - "is-glob": "^4.0.3", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "dev": true, - "requires": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", - "dev": true, - "requires": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true, - "requires": {} - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - } - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, - "requires": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-what": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", - "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "jose": { - "version": "4.10.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.10.4.tgz", - "integrity": "sha512-eBH77Xs9Yc/oTDvukhAEDVMijhekPuNktXJL4tUlB22jqKP1k48v5nmsUmc8feoJPsxB3HsfEt2LbVSoz+1mng==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "langium": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/langium/-/langium-0.5.0.tgz", - "integrity": "sha512-7mheQQtyyPm8r9R+8sPZopcdzEcHtCC+M52m8ZbotzNql8RPLs3+QbDY6EZ98ejKtJCGt9RviTkfZEt1LxX86w==", - "dev": true, - "requires": { - "chevrotain": "^9.1.0", - "vscode-languageserver": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.7", - "vscode-uri": "^3.0.2" - } - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true - }, - "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dev": true, - "requires": { - "language-subtag-registry": "~0.3.2" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "dependencies": { - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true - } - } - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - }, - "mixpanel": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.17.0.tgz", - "integrity": "sha512-DY5WeOy/hmkPrNiiZugJpWR0iMuOwuj1a3u0bgwB2eUFRV6oIew/pIahhpawdbNjb+Bye4a8ID3gefeNPvL81g==", - "dev": true, - "requires": { - "https-proxy-agent": "5.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz", - "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", - "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", - "requires": { - "@next/env": "12.3.1", - "@next/swc-android-arm-eabi": "12.3.1", - "@next/swc-android-arm64": "12.3.1", - "@next/swc-darwin-arm64": "12.3.1", - "@next/swc-darwin-x64": "12.3.1", - "@next/swc-freebsd-x64": "12.3.1", - "@next/swc-linux-arm-gnueabihf": "12.3.1", - "@next/swc-linux-arm64-gnu": "12.3.1", - "@next/swc-linux-arm64-musl": "12.3.1", - "@next/swc-linux-x64-gnu": "12.3.1", - "@next/swc-linux-x64-musl": "12.3.1", - "@next/swc-win32-arm64-msvc": "12.3.1", - "@next/swc-win32-ia32-msvc": "12.3.1", - "@next/swc-win32-x64-msvc": "12.3.1", - "@swc/helpers": "0.4.11", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.0.7", - "use-sync-external-store": "1.2.0" - }, - "dependencies": { - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - } - } - }, - "next-auth": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.15.1.tgz", - "integrity": "sha512-yB/cSEqslaXAsLZ3lvkJbB7bRR5JeZvfUXSw0CmWeP+TaMZWB85fG9PWdfBeFrDCLBsoyATw9FF6fzApE0SxSw==", - "requires": { - "@babel/runtime": "^7.16.3", - "@panva/hkdf": "^1.0.1", - "cookie": "^0.5.0", - "jose": "^4.9.3", - "oauth": "^0.9.15", - "openid-client": "^5.1.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" - } - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true - }, - "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" - }, - "oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", - "dev": true, - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "oidc-token-hash": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", - "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "openid-client": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.2.1.tgz", - "integrity": "sha512-KPxqWnxobG/70Cxqyvd43RWfCfHedFnCdHSBpw5f7WnTnuBAeBnvot/BIo+brrcTr0wyAYUlL/qejQSGwWtdIg==", - "requires": { - "jose": "^4.10.0", - "lru-cache": "^6.0.0", - "object-hash": "^2.0.1", - "oidc-token-hash": "^5.0.1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "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.3" - } - }, - "ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", - "dev": true, - "requires": { - "bl": "^5.0.0", - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" - }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true - }, - "postcss": { - "version": "8.4.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", - "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "dependencies": { - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - } - } - }, - "postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - } - }, - "postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "preact": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.2.tgz", - "integrity": "sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw==" - }, - "preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", - "requires": { - "pretty-format": "^3.8.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" - }, - "prisma": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.0.tgz", - "integrity": "sha512-VsecNo0Ca3+bDTzSpJqIpdupKVhhQ8aOYeWc09JlUM89knqvhSrlMrg0U8BiOD4tFrY1OPaCcraK8leDBxKMBg==", - "devOptional": true, - "requires": { - "@prisma/engines": "4.7.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promisify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/promisify/-/promisify-0.0.3.tgz", - "integrity": "sha512-CcBGsRhhq466fsZVyHfptuKqon6eih0CqMsJE0kWIIjbpVNEyDoaKLELm2WVs//W/WXRBHip+6xhTExTkHUwtA==", - "dev": true, - "requires": { - "when": "" - } - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "react-toastify": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.8.tgz", - "integrity": "sha512-EwM+teWt49HSHx+67qI08yLAW1zAsBxCXLCsUfxHYv1W7/R3ZLhrqKalh7j+kjgPna1h5LQMSMwns4tB4ww2yQ==", - "requires": { - "clsx": "^1.1.1" - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "requires": { - "pify": "^2.3.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" - }, - "regexp-to-ast": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", - "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sleep-promise": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", - "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - } - } - }, - "string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "styled-jsx": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", - "requires": {} - }, - "superjson": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.11.0.tgz", - "integrity": "sha512-6PfAg1FKhqkwWvPb2uXhH4MkMttdc17eJ91+Aoz4s1XUEDZFmLfFx/xVA3wgkPxAGy5dpozgGdK6V/n20Wj9yg==", - "requires": { - "copy-anything": "^3.0.2" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "swr": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz", - "integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==", - "requires": {} - }, - "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "tailwindcss": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.0.tgz", - "integrity": "sha512-ARh/W0uH5UlWIC2nn02V0+5fyF0k6qZliyt4QYic2upOhPUE/Spu1EURNc9txJ3+4j8OEmdigqfDpw4d2tA4vA==", - "requires": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.17", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-morph": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-16.0.0.tgz", - "integrity": "sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==", - "dev": true, - "requires": { - "@ts-morph/common": "~0.17.0", - "code-block-writer": "^11.0.3" - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - } - }, - "upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "requires": {} - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", - "dev": true - }, - "vscode-languageclient": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", - "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", - "dev": true, - "requires": { - "minimatch": "^3.0.4", - "semver": "^7.3.5", - "vscode-languageserver-protocol": "3.17.2" - } - }, - "vscode-languageserver": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", - "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", - "dev": true, - "requires": { - "vscode-languageserver-protocol": "3.17.2" - } - }, - "vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", - "dev": true, - "requires": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" - } - }, - "vscode-languageserver-textdocument": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", - "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==", - "dev": true - }, - "vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==", - "dev": true - }, - "vscode-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz", - "integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==", - "dev": true - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "zenstack": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.5.0.tgz", - "integrity": "sha512-nMYl5b9/9FuKBQP++xnJoeM/9bySavgfeRtT552BP7S8I9f8iRK/SxMJqaD8IS5mdFhXj3sdQULjd43euLs8wA==", - "dev": true, - "requires": { - "@zenstackhq/runtime": "0.5.0", - "async-exit-hook": "^2.0.1", - "change-case": "^4.1.2", - "chevrotain": "^9.1.0", - "colors": "1.4.0", - "commander": "^8.3.0", - "cuid": "^2.1.8", - "langium": "^0.5.0", - "mixpanel": "^0.17.0", - "node-machine-id": "^1.1.12", - "ora": "^6.1.2", - "pluralize": "^8.0.0", - "prisma": "~4.7.0", - "promisify": "^0.0.3", - "sleep-promise": "^9.1.0", - "ts-morph": "^16.0.0", - "uuid": "^9.0.0", - "vscode-jsonrpc": "^8.0.2", - "vscode-languageclient": "^8.0.2", - "vscode-languageserver": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.7", - "vscode-uri": "^3.0.6" - }, - "dependencies": { - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "dev": true - } - } - }, - "zod": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz", - "integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==" - }, - "zod-validation-error": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-0.2.1.tgz", - "integrity": "sha512-zGg6P5EHi5V0dvyEeC8HBZd2pzp7QDKTngkSWgWunljrY+0SHkHyjI519D+u8/37BHkGHAFseWgnZ2Uq8LNFKg==", - "requires": { - "@swc/helpers": "^0.4.11" - } - } - } -} diff --git a/samples/todo/package.json b/samples/todo/package.json deleted file mode 100644 index 07f2c86d1..000000000 --- a/samples/todo/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "todo", - "version": "0.5.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "npm run generate && next build", - "start": "next start", - "lint": "next lint", - "db:push": "zenstack db push", - "db:migrate": "zenstack migrate dev", - "db:deploy": "zenstack migrate deploy", - "db:reset": "zenstack migrate reset", - "db:browse": "zenstack studio", - "generate": "zenstack generate", - "vercel-build": "npm run build && npm run db:deploy", - "deps-local": "npm i -D ../../packages/schema && npm i ../../packages/runtime/dist ../../packages/next-auth/dist zod zod-validation-error swr", - "deps-latest": "npm rm zod zod-validation-error swr && npm i -D zenstack@latest && npm i @zenstackhq/runtime@latest @zenstackhq/next-auth@latest", - "deps-dev": "npm rm zod zod-validation-error swr && npm i -D zenstack@dev && npm i @zenstackhq/runtime@dev @zenstackhq/next-auth@dev" - }, - "dependencies": { - "@heroicons/react": "^2.0.12", - "@prisma/client": "^4.7.0", - "@zenstackhq/next-auth": "^0.5.0", - "@zenstackhq/runtime": "^0.5.0", - "babel-plugin-superjson-next": "^0.4.5", - "bcryptjs": "^2.4.3", - "daisyui": "^2.31.0", - "moment": "^2.29.4", - "nanoid": "^4.0.0", - "next": "12.3.1", - "next-auth": "^4.15.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-toastify": "^9.0.8" - }, - "devDependencies": { - "@tailwindcss/line-clamp": "^0.4.2", - "@types/bcryptjs": "^2.4.2", - "@types/node": "^14.17.3", - "@types/react": "18.0.21", - "@types/react-dom": "18.0.6", - "autoprefixer": "^10.4.12", - "eslint": "^7.19.0", - "eslint-config-next": "12.3.1", - "postcss": "^8.4.16", - "tailwindcss": "^3.1.8", - "typescript": "^4.6.2", - "zenstack": "^0.5.0" - } -} diff --git a/samples/todo/pages/_app.tsx b/samples/todo/pages/_app.tsx deleted file mode 100644 index defec6cbe..000000000 --- a/samples/todo/pages/_app.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import '../styles/globals.css'; -import type { AppProps } from 'next/app'; -import { SessionProvider } from 'next-auth/react'; -import 'react-toastify/dist/ReactToastify.css'; -import { ToastContainer } from 'react-toastify'; -import { - SpaceContext, - useCurrentSpace, - useCurrentUser, - UserContext, -} from '@lib/context'; -import AuthGuard from 'components/AuthGuard'; - -function AppContent(props: { children: JSX.Element | JSX.Element[] }) { - const user = useCurrentUser(); - const space = useCurrentSpace(); - - return ( - - - -
- {props.children} -
-
-
-
- ); -} - -function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) { - return ( - - -
- - -
-
-
- ); -} - -export default MyApp; diff --git a/samples/todo/pages/api/auth/[...nextauth].ts b/samples/todo/pages/api/auth/[...nextauth].ts deleted file mode 100644 index f1a2dccac..000000000 --- a/samples/todo/pages/api/auth/[...nextauth].ts +++ /dev/null @@ -1,88 +0,0 @@ -import NextAuth, { NextAuthOptions, User } from 'next-auth'; -import CredentialsProvider from 'next-auth/providers/credentials'; -import GitHubProvider from 'next-auth/providers/github'; -import { authorize, Adapter } from '@zenstackhq/next-auth'; -import service from '@zenstackhq/runtime/server'; -import { nanoid } from 'nanoid'; -import { SpaceUserRole } from '@zenstackhq/runtime/types'; - -export const authOptions: NextAuthOptions = { - // Configure one or more authentication providers - - adapter: Adapter(service), - - session: { - strategy: 'jwt', - }, - - pages: { - signIn: '/signin', - }, - - providers: [ - CredentialsProvider({ - credentials: { - email: { - type: 'email', - }, - password: { - type: 'password', - }, - }, - authorize: authorize(service), - }), - - GitHubProvider({ - clientId: process.env.GITHUB_ID!, - clientSecret: process.env.GITHUB_SECRET!, - // @ts-ignore - scope: 'read:user,user:email', - }), - ], - - callbacks: { - async session({ session, token }) { - return { - ...session, - user: { - ...session.user, - id: token.sub!, - }, - }; - }, - }, - - events: { - async signIn({ user }: { user: User }) { - const spaceCount = await service.db.spaceUser.count({ - where: { - userId: user.id, - }, - }); - if (spaceCount > 0) { - return; - } - - console.log( - `User ${user.id} doesn't belong to any space. Creating one.` - ); - const space = await service.db.space.create({ - data: { - name: `${user.name || user.email}'s space`, - slug: nanoid(8), - members: { - create: [ - { - userId: user.id, - role: SpaceUserRole.ADMIN, - }, - ], - }, - }, - }); - console.log(`Space created:`, space); - }, - }, -}; - -export default NextAuth(authOptions); diff --git a/samples/todo/pages/api/zenstack/[...path].ts b/samples/todo/pages/api/zenstack/[...path].ts deleted file mode 100644 index cdae27d69..000000000 --- a/samples/todo/pages/api/zenstack/[...path].ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; -import { - type RequestHandlerOptions, - requestHandler, -} from '@zenstackhq/runtime/server'; -import { authOptions } from '@api/auth/[...nextauth]'; -import { unstable_getServerSession } from 'next-auth'; -import service from '@zenstackhq/runtime/server'; - -const options: RequestHandlerOptions = { - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - const session = await unstable_getServerSession(req, res, authOptions); - return session?.user; - }, -}; -export default requestHandler(service, options); diff --git a/samples/todo/pages/create-space.tsx b/samples/todo/pages/create-space.tsx deleted file mode 100644 index 3839fdafd..000000000 --- a/samples/todo/pages/create-space.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { - ServerErrorCode, - useSpace, - type HooksError, -} from '@zenstackhq/runtime/client'; -import { SpaceUserRole } from '@zenstackhq/runtime/types'; -import WithNavBar from 'components/WithNavBar'; -import { NextPage } from 'next'; -import { useSession } from 'next-auth/react'; -import { useRouter } from 'next/router'; -import { FormEvent, useState } from 'react'; -import { toast } from 'react-toastify'; - -const CreateSpace: NextPage = () => { - const { data: session } = useSession(); - const [name, setName] = useState(''); - const [slug, setSlug] = useState(''); - - const { create } = useSpace(); - const router = useRouter(); - - const onSubmit = async (event: FormEvent) => { - event.preventDefault(); - try { - const space = await create({ - data: { - name, - slug, - members: { - create: [ - { - userId: session!.user.id, - role: SpaceUserRole.ADMIN, - }, - ], - }, - }, - }); - console.log('Space created:', space); - toast.success("Space created successfull! You'll be redirected."); - - setTimeout(() => { - if (space) { - router.push(`/space/${space.slug}`); - } - }, 2000); - } catch (err: any) { - console.error(err); - if ( - (err as HooksError).info?.code === - ServerErrorCode.UNIQUE_CONSTRAINT_VIOLATION - ) { - toast.error('Space slug alread in use'); - } else { - toast.error( - `Error occurred: ${err.info?.message || err.message}` - ); - } - } - }; - - return ( - -
-
-

Create a space

-
-
- - ) => - setName(e.currentTarget.value) - } - /> -
-
- - ) => - setSlug(e.currentTarget.value) - } - /> -
-
- -
- 20 || - !slug.match(/^[0-9a-zA-Z]{4,16}$/) - } - value="Create" - className="btn btn-primary px-8" - /> - -
-
-
-
- ); -}; - -export default CreateSpace; diff --git a/samples/todo/pages/index.tsx b/samples/todo/pages/index.tsx deleted file mode 100644 index a96a07c21..000000000 --- a/samples/todo/pages/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { authOptions } from '@api/auth/[...nextauth]'; -import { useCurrentUser } from '@lib/context'; -import service from '@zenstackhq/runtime/server'; -import { Space } from '@zenstackhq/runtime/types'; -import Spaces from 'components/Spaces'; -import WithNavBar from 'components/WithNavBar'; -import type { GetServerSideProps, NextPage } from 'next'; -import { unstable_getServerSession } from 'next-auth'; -import Link from 'next/link'; - -type Props = { - spaces: Space[]; -}; - -const Home: NextPage = ({ spaces }) => { - const user = useCurrentUser(); - return ( - - {user && ( -
-

- Welcome {user.name || user.email}! -

-
-

- Choose a space to start, or{' '} - - - create a new one. - - -

- -
-
- )} -
- ); -}; - -export const getServerSideProps: GetServerSideProps = async ({ - req, - res, -}) => { - const session = await unstable_getServerSession(req, res, authOptions); - const spaces = await service.space.find({ user: session?.user }); - return { - props: { spaces }, - }; -}; - -export default Home; diff --git a/samples/todo/pages/signin.tsx b/samples/todo/pages/signin.tsx deleted file mode 100644 index 4f0c7fe9d..000000000 --- a/samples/todo/pages/signin.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import { FormEvent, useEffect, useState } from 'react'; -import { toast } from 'react-toastify'; -import { signIn } from 'next-auth/react'; -import { useRouter } from 'next/router'; - -export default function Signup() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const router = useRouter(); - - useEffect(() => { - if (router.query.error) { - if (router.query.error === 'OAuthCreateAccount') { - toast.error( - 'Unable to signin. The user email may be already in use.' - ); - } else { - toast.error(`Authentication error: ${router.query.error}`); - } - } - }, [router]); - - async function onSignin(e: FormEvent) { - e.preventDefault(); - const signInResult = await signIn('credentials', { - redirect: false, - email, - password, - }); - if (signInResult?.ok) { - window.location.href = '/'; - } else { - toast.error(`Signin failed. Please check your email and password.`); - } - } - - return ( -
- -
- logo -

Welcome to Todo

-
- -
-
-

- Sign in to your account -

- -
onSignin(e)} - > -
- - setEmail(e.target.value)} - className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5" - placeholder="Email address" - required - /> -
-
- - setPassword(e.target.value)} - placeholder="••••••••" - className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5" - required - /> -
-
-
- -
-
- -
-
- -
- - -
- signIn('github', { callbackUrl: '/' }) - } - > - Sign in with GitHub -
-
- -
- Not registered?{' '} - - Create account - -
-
-
-
-
- ); -} diff --git a/samples/todo/pages/signup.tsx b/samples/todo/pages/signup.tsx deleted file mode 100644 index 4d108eabc..000000000 --- a/samples/todo/pages/signup.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import { FormEvent, useState } from 'react'; -import { - HooksError, - ServerErrorCode, - useUser, -} from '@zenstackhq/runtime/client'; -import { toast } from 'react-toastify'; -import { signIn } from 'next-auth/react'; - -export default function Signup() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const { create: signup } = useUser(); - - async function onSignup(e: FormEvent) { - e.preventDefault(); - try { - await signup({ data: { email, password } }); - } catch (err: any) { - console.error(err); - if ( - (err as HooksError).info?.code === - ServerErrorCode.UNIQUE_CONSTRAINT_VIOLATION - ) { - toast.error('User already exists'); - } else { - toast.error( - `Error occurred: ${err.info?.message || err.message}` - ); - } - return; - } - - const signInResult = await signIn('credentials', { - redirect: false, - email, - password, - }); - if (signInResult?.ok) { - window.location.href = '/'; - } else { - console.error('Signin failed:', signInResult?.error); - } - } - - return ( -
- -
- logo -

Welcome to Todo

-
- -
-
-

- Create a Free Account -

-
onSignup(e)} - > -
- - setEmail(e.target.value)} - className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5" - placeholder="Email address" - required - /> -
-
- - setPassword(e.target.value)} - placeholder="••••••••" - className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5" - required - /> -
-
-
- -
-
- -
-
- -
- Already have an account?{' '} - - Login here - -
-
-
-
-
- ); -} diff --git a/samples/todo/pages/space/[slug]/[listId]/index.tsx b/samples/todo/pages/space/[slug]/[listId]/index.tsx deleted file mode 100644 index d07ab5367..000000000 --- a/samples/todo/pages/space/[slug]/[listId]/index.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { authOptions } from '@api/auth/[...nextauth]'; -import { useTodo } from '@zenstackhq/runtime/client'; -import { PlusIcon } from '@heroicons/react/24/outline'; -import { ChangeEvent, KeyboardEvent, useState } from 'react'; -import { useCurrentUser } from '@lib/context'; -import TodoComponent from 'components/Todo'; -import BreadCrumb from 'components/BreadCrumb'; -import WithNavBar from 'components/WithNavBar'; -import { List, Space, Todo, User } from '@zenstackhq/runtime/types'; -import { GetServerSideProps } from 'next'; -import { unstable_getServerSession } from 'next-auth'; -import service from '@zenstackhq/runtime/server'; -import { getSpaceBySlug } from '@lib/query-utils'; -import { toast } from 'react-toastify'; - -type Props = { - space: Space; - list: List; - todos: (Todo & { owner: User })[]; -}; - -export default function TodoList(props: Props) { - const user = useCurrentUser(); - const { create: createTodo, find: findTodos } = useTodo(); - const [title, setTitle] = useState(''); - - const { data: todos, mutate: invalidateTodos } = findTodos( - { - where: { - listId: props.list.id, - }, - include: { - owner: true, - }, - orderBy: { - updatedAt: 'desc', - }, - }, - { initialData: props.todos } - ); - - const _createTodo = async () => { - try { - const todo = await createTodo({ - data: { - title, - ownerId: user!.id, - listId: props.list.id, - }, - }); - console.log(`Todo created: ${todo}`); - setTitle(''); - } catch (err: any) { - toast.error( - `Failed to create todo: ${err.info?.message || err.message}` - ); - } - }; - - return ( - -
- -
-
-

- {props.list?.title} -

-
- ) => { - if (e.key === 'Enter') { - _createTodo(); - } - }} - onChange={(e: ChangeEvent) => { - setTitle(e.currentTarget.value); - }} - /> - -
-
    - {todos?.map((todo) => ( - { - invalidateTodos(); - }} - deleted={() => { - invalidateTodos(); - }} - /> - ))} -
-
-
- ); -} - -export const getServerSideProps: GetServerSideProps = async ({ - req, - res, - params, -}) => { - const session = await unstable_getServerSession(req, res, authOptions); - const queryContext = { user: session?.user }; - - const space = await getSpaceBySlug(queryContext, params?.slug as string); - - const list = await service.list.get(queryContext, params?.listId as string); - if (!list) { - throw new Error(`List not found: ${params?.listId}`); - } - - const todos = await service.todo.find(queryContext, { - where: { - listId: params?.id as string, - }, - include: { - owner: true, - }, - orderBy: { - updatedAt: 'desc', - }, - }); - - return { - props: { space, list, todos }, - }; -}; diff --git a/samples/todo/pages/space/[slug]/index.tsx b/samples/todo/pages/space/[slug]/index.tsx deleted file mode 100644 index 9f767374c..000000000 --- a/samples/todo/pages/space/[slug]/index.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { authOptions } from '@api/auth/[...nextauth]'; -import { SpaceContext, UserContext } from '@lib/context'; -import { - ChangeEvent, - FormEvent, - useContext, - useEffect, - useRef, - useState, -} from 'react'; -import { useList } from '@zenstackhq/runtime/client'; -import { toast } from 'react-toastify'; -import TodoList from 'components/TodoList'; -import BreadCrumb from 'components/BreadCrumb'; -import SpaceMembers from 'components/SpaceMembers'; -import WithNavBar from 'components/WithNavBar'; -import { List, Space, User } from '@zenstackhq/runtime/types'; -import { GetServerSideProps } from 'next'; -import { unstable_getServerSession } from 'next-auth'; -import service from '@zenstackhq/runtime/server'; -import { useRouter } from 'next/router'; -import { getSpaceBySlug } from '@lib/query-utils'; - -function CreateDialog() { - const user = useContext(UserContext); - const space = useContext(SpaceContext); - - const [modalOpen, setModalOpen] = useState(false); - const [title, setTitle] = useState(''); - const [_private, setPrivate] = useState(false); - - const { create } = useList(); - const inputRef = useRef(null); - - useEffect(() => { - if (modalOpen) { - inputRef.current?.focus(); - } - }, [modalOpen]); - - const onSubmit = async (event: FormEvent) => { - event.preventDefault(); - - try { - await create({ - data: { - title, - private: _private, - spaceId: space!.id, - ownerId: user!.id, - }, - }); - } catch (err: any) { - toast.error( - `Failed to create list: ${err.info?.message || err.message}` - ); - return; - } - - toast.success('List created successfully!'); - - // reset states - setTitle(''); - setPrivate(false); - - // close modal - setModalOpen(false); - }; - - return ( - <> - ) => { - setModalOpen(e.currentTarget.checked); - }} - /> -
-
-

- Create a Todo list -

-
-
-
- - - ) => setTitle(e.currentTarget.value)} - /> -
-
- - - ) => setPrivate(e.currentTarget.checked)} - /> -
-
-
- - -
-
-
-
- - ); -} - -type Props = { - space: Space; - lists: (List & { owner: User })[]; -}; - -export default function SpaceHome(props: Props) { - const space = useContext(SpaceContext); - const { find } = useList(); - const router = useRouter(); - - const { data: lists, mutate: invalidateLists } = find( - { - where: { - space: { - slug: router.query.slug as string, - }, - }, - include: { - owner: true, - }, - orderBy: { - updatedAt: 'desc', - }, - }, - { - disabled: !space, - initialData: props.lists, - } - ); - - return ( - -
- -
-
-
- - -
- -
    - {lists?.map((list) => ( -
  • - invalidateLists()} - /> -
  • - ))} -
- - -
-
- ); -} - -export const getServerSideProps: GetServerSideProps = async ({ - req, - res, - params, -}) => { - const session = await unstable_getServerSession(req, res, authOptions); - const queryContext = { user: session?.user }; - - const space = await getSpaceBySlug(queryContext, params?.slug as string); - - const lists = await service.list.find(queryContext, { - where: { - space: { - slug: params?.slug as string, - }, - }, - include: { - owner: true, - }, - orderBy: { - updatedAt: 'desc', - }, - }); - return { - props: { space, lists }, - }; -}; diff --git a/samples/todo/postcss.config.js b/samples/todo/postcss.config.js deleted file mode 100644 index 33ad091d2..000000000 --- a/samples/todo/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/samples/todo/public/auth-bg.jpg b/samples/todo/public/auth-bg.jpg deleted file mode 100644 index cf14a7486..000000000 Binary files a/samples/todo/public/auth-bg.jpg and /dev/null differ diff --git a/samples/todo/public/avatar.jpg b/samples/todo/public/avatar.jpg deleted file mode 100644 index 34e82838c..000000000 Binary files a/samples/todo/public/avatar.jpg and /dev/null differ diff --git a/samples/todo/public/favicon.ico b/samples/todo/public/favicon.ico deleted file mode 100644 index 718d6fea4..000000000 Binary files a/samples/todo/public/favicon.ico and /dev/null differ diff --git a/samples/todo/public/logo.png b/samples/todo/public/logo.png deleted file mode 100644 index 48bbe06d9..000000000 Binary files a/samples/todo/public/logo.png and /dev/null differ diff --git a/samples/todo/public/vercel.svg b/samples/todo/public/vercel.svg deleted file mode 100644 index fbf0e25a6..000000000 --- a/samples/todo/public/vercel.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/samples/todo/styles/globals.css b/samples/todo/styles/globals.css deleted file mode 100644 index 1d6a5711d..000000000 --- a/samples/todo/styles/globals.css +++ /dev/null @@ -1,7 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - @apply text-gray-800; -} diff --git a/samples/todo/tailwind.config.js b/samples/todo/tailwind.config.js deleted file mode 100644 index d8efff5e9..000000000 --- a/samples/todo/tailwind.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - './pages/**/*.{js,ts,jsx,tsx}', - './components/**/*.{js,ts,jsx,tsx}', - ], - theme: { - extend: {}, - }, - plugins: [require('daisyui'), require('@tailwindcss/line-clamp')], -}; diff --git a/samples/todo/tsconfig.json b/samples/todo/tsconfig.json deleted file mode 100644 index cdfdd1e3e..000000000 --- a/samples/todo/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "baseUrl": ".", - "paths": { - "@api/*": ["pages/api/*"], - "@lib/*": ["lib/*"], - "@components/*": ["lib/components/*"], - "@components": ["lib/components/index"], - ".zenstack/*": ["node_modules/.zenstack/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "zenstack.config.json"], - "exclude": ["node_modules"] -} diff --git a/samples/todo/types/next-auth.d.ts b/samples/todo/types/next-auth.d.ts deleted file mode 100644 index 891911794..000000000 --- a/samples/todo/types/next-auth.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Session } from 'next-auth'; -import { JWT } from 'next-auth/jwt'; - -/** Example on how to extend the built-in session types */ -declare module 'next-auth' { - interface Session { - user: { id: string; name: string; email: string; image?: string }; - } -} - -/** Example on how to extend the built-in types for JWT */ -declare module 'next-auth/jwt' { - interface JWT {} -} diff --git a/samples/todo/types/next.d.ts b/samples/todo/types/next.d.ts deleted file mode 100644 index 777c0a0be..000000000 --- a/samples/todo/types/next.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { NextComponentType, NextPageContext } from 'next'; -import type { Session } from 'next-auth'; -import type { Router } from 'next/router'; - -declare module 'next/app' { - type AppProps

> = { - Component: NextComponentType; - router: Router; - __N_SSG?: boolean; - __N_SSP?: boolean; - pageProps: P & { - /** Initial session passed in from `getServerSideProps` or `getInitialProps` */ - session?: Session; - }; - }; -} diff --git a/samples/todo/zenstack.config.json b/samples/todo/zenstack.config.json deleted file mode 100644 index b1b6cafaf..000000000 --- a/samples/todo/zenstack.config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "log": ["info", "warn", "error"] -} diff --git a/samples/todo/zenstack/migrations/20221014084317_init/migration.sql b/samples/todo/zenstack/migrations/20221014084317_init/migration.sql deleted file mode 100644 index 6ebcb561e..000000000 --- a/samples/todo/zenstack/migrations/20221014084317_init/migration.sql +++ /dev/null @@ -1,153 +0,0 @@ --- CreateEnum -CREATE TYPE "SpaceUserRole" AS ENUM ('USER', 'ADMIN'); - --- CreateTable -CREATE TABLE "Space" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "name" TEXT NOT NULL, - "slug" TEXT NOT NULL, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "Space_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "SpaceUser" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "spaceId" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "role" "SpaceUserRole" NOT NULL, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "SpaceUser_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "User" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "email" TEXT NOT NULL, - "emailVerified" TIMESTAMP(3), - "password" TEXT, - "name" TEXT, - "image" TEXT, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "List" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "spaceId" TEXT NOT NULL, - "ownerId" TEXT NOT NULL, - "title" TEXT NOT NULL, - "private" BOOLEAN NOT NULL DEFAULT false, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "List_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Todo" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "ownerId" TEXT NOT NULL, - "listId" TEXT NOT NULL, - "title" TEXT NOT NULL, - "completedAt" TIMESTAMP(3), - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "Todo_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Account" ( - "id" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "type" TEXT NOT NULL, - "provider" TEXT NOT NULL, - "providerAccountId" TEXT NOT NULL, - "refresh_token" TEXT, - "access_token" TEXT, - "expires_at" INTEGER, - "token_type" TEXT, - "scope" TEXT, - "id_token" TEXT, - "session_state" TEXT, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "Account_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Session" ( - "id" TEXT NOT NULL, - "sessionToken" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "expires" TIMESTAMP(3) NOT NULL, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "Session_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "VerificationToken" ( - "identifier" TEXT NOT NULL, - "token" TEXT NOT NULL, - "expires" TIMESTAMP(3) NOT NULL, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true -); - --- CreateIndex -CREATE UNIQUE INDEX "Space_slug_key" ON "Space"("slug"); - --- CreateIndex -CREATE UNIQUE INDEX "SpaceUser_userId_spaceId_key" ON "SpaceUser"("userId", "spaceId"); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); - --- CreateIndex -CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); - --- CreateIndex -CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); - --- CreateIndex -CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token"); - --- AddForeignKey -ALTER TABLE "SpaceUser" ADD CONSTRAINT "SpaceUser_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "Space"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "SpaceUser" ADD CONSTRAINT "SpaceUser_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "List" ADD CONSTRAINT "List_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "Space"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "List" ADD CONSTRAINT "List_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Todo" ADD CONSTRAINT "Todo_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Todo" ADD CONSTRAINT "Todo_listId_fkey" FOREIGN KEY ("listId") REFERENCES "List"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql b/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql deleted file mode 100644 index 8a0dbef77..000000000 --- a/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ --- AlterTable -ALTER TABLE "Account" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "List" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "Session" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "Space" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "SpaceUser" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "Todo" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "User" ADD COLUMN "zenstack_transaction" TEXT; - --- AlterTable -ALTER TABLE "VerificationToken" ADD COLUMN "zenstack_transaction" TEXT; diff --git a/samples/todo/zenstack/migrations/20221103144245_drop_account_session/migration.sql b/samples/todo/zenstack/migrations/20221103144245_drop_account_session/migration.sql deleted file mode 100644 index dd08eae20..000000000 --- a/samples/todo/zenstack/migrations/20221103144245_drop_account_session/migration.sql +++ /dev/null @@ -1,41 +0,0 @@ -/* - Warnings: - - - You are about to drop the `Account` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `Session` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `VerificationToken` table. If the table is not empty, all the data it contains will be lost. - - Made the column `password` on table `User` required. This step will fail if there are existing NULL values in that column. - -*/ --- DropForeignKey -ALTER TABLE "Account" DROP CONSTRAINT "Account_userId_fkey"; - --- DropForeignKey -ALTER TABLE "Session" DROP CONSTRAINT "Session_userId_fkey"; - --- AlterTable -ALTER TABLE "User" ALTER COLUMN "password" SET NOT NULL; - --- DropTable -DROP TABLE "Account"; - --- DropTable -DROP TABLE "Session"; - --- DropTable -DROP TABLE "VerificationToken"; - --- CreateIndex -CREATE INDEX "List_zenstack_transaction_idx" ON "List"("zenstack_transaction"); - --- CreateIndex -CREATE INDEX "Space_zenstack_transaction_idx" ON "Space"("zenstack_transaction"); - --- CreateIndex -CREATE INDEX "SpaceUser_zenstack_transaction_idx" ON "SpaceUser"("zenstack_transaction"); - --- CreateIndex -CREATE INDEX "Todo_zenstack_transaction_idx" ON "Todo"("zenstack_transaction"); - --- CreateIndex -CREATE INDEX "User_zenstack_transaction_idx" ON "User"("zenstack_transaction"); diff --git a/samples/todo/zenstack/migrations/20221126150023_add_account/migration.sql b/samples/todo/zenstack/migrations/20221126150023_add_account/migration.sql deleted file mode 100644 index 1ca5b13b9..000000000 --- a/samples/todo/zenstack/migrations/20221126150023_add_account/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ --- CreateTable -CREATE TABLE "Account" ( - "id" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "type" TEXT NOT NULL, - "provider" TEXT NOT NULL, - "providerAccountId" TEXT NOT NULL, - "refresh_token" TEXT, - "access_token" TEXT, - "expires_at" INTEGER, - "token_type" TEXT, - "scope" TEXT, - "id_token" TEXT, - "session_state" TEXT, - "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, - "zenstack_transaction" TEXT, - - CONSTRAINT "Account_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "Account_zenstack_transaction_idx" ON "Account"("zenstack_transaction"); - --- CreateIndex -CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); - --- AddForeignKey -ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/samples/todo/zenstack/migrations/20221126151212_email_password_optional/migration.sql b/samples/todo/zenstack/migrations/20221126151212_email_password_optional/migration.sql deleted file mode 100644 index 461205f1d..000000000 --- a/samples/todo/zenstack/migrations/20221126151212_email_password_optional/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "User" ALTER COLUMN "email" DROP NOT NULL, -ALTER COLUMN "password" DROP NOT NULL; diff --git a/samples/todo/zenstack/migrations/20221126151510_refresh_token_expires/migration.sql b/samples/todo/zenstack/migrations/20221126151510_refresh_token_expires/migration.sql deleted file mode 100644 index bdc7f4f49..000000000 --- a/samples/todo/zenstack/migrations/20221126151510_refresh_token_expires/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Account" ADD COLUMN "refresh_token_expires_in" INTEGER; diff --git a/samples/todo/zenstack/migrations/20221127033222_email_required/migration.sql b/samples/todo/zenstack/migrations/20221127033222_email_required/migration.sql deleted file mode 100644 index ab71b3693..000000000 --- a/samples/todo/zenstack/migrations/20221127033222_email_required/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* - Warnings: - - - Made the column `email` on table `User` required. This step will fail if there are existing NULL values in that column. - -*/ --- AlterTable -ALTER TABLE "User" ALTER COLUMN "email" SET NOT NULL; diff --git a/samples/todo/zenstack/migrations/migration_lock.toml b/samples/todo/zenstack/migrations/migration_lock.toml deleted file mode 100644 index fbffa92c2..000000000 --- a/samples/todo/zenstack/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/tests/integration/.eslintrc.json b/tests/integration/.eslintrc.json new file mode 100644 index 000000000..24ebad85a --- /dev/null +++ b/tests/integration/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "extends": ["plugin:jest/recommended"], + "rules": { + "jest/expect-expect": "off" + } +} diff --git a/tests/integration/.gitignore b/tests/integration/.gitignore index 08d1baceb..01f5e1303 100644 --- a/tests/integration/.gitignore +++ b/tests/integration/.gitignore @@ -1,2 +1,4 @@ .npmcache -tests/test-run +node_modules +test-run/cases +tests/**/test-run diff --git a/tests/integration/global-setup.js b/tests/integration/global-setup.js new file mode 100644 index 000000000..0d4b8e23e --- /dev/null +++ b/tests/integration/global-setup.js @@ -0,0 +1,10 @@ +const { execSync } = require('child_process'); + +module.exports = function () { + console.log('npm install'); + execSync('npm install', { + encoding: 'utf-8', + stdio: 'inherit', + cwd: 'test-run', + }); +}; diff --git a/tests/integration/jest.config.ts b/tests/integration/jest.config.ts index e825ade7c..9f13cb6b8 100644 --- a/tests/integration/jest.config.ts +++ b/tests/integration/jest.config.ts @@ -11,6 +11,7 @@ export default { testTimeout: 300000, - // explicitly specify moduel paths so that resolution from local dependencies work - modulePaths: ['/tests/test-run/node_modules'], + globalSetup: './global-setup.js', + + setupFilesAfterEnv: ['./test-setup.ts'], }; diff --git a/tests/integration/jest.d.ts b/tests/integration/jest.d.ts new file mode 100644 index 000000000..ff066029a --- /dev/null +++ b/tests/integration/jest.d.ts @@ -0,0 +1,16 @@ +interface CustomMatchers { + toBeRejectedByPolicy(expectedMessages?: string[]): Promise; + toBeNotFound(): Promise; + toResolveTruthy(): Promise; + toResolveFalsy(): Promise; + toResolveNull(): Promise; + toBeRejectedWithCode(code: string): Promise; +} +declare global { + namespace jest { + interface Expect extends CustomMatchers {} + interface Matchers extends CustomMatchers {} + interface InverseAsymmetricMatchers extends CustomMatchers {} + } +} +export {}; diff --git a/tests/integration/package.json b/tests/integration/package.json index b1c4a83de..821e067ff 100644 --- a/tests/integration/package.json +++ b/tests/integration/package.json @@ -4,26 +4,39 @@ "description": "", "main": "index.js", "scripts": { - "test": "jest --runInBand" + "lint": "eslint . --ext .ts", + "test": "jest" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@types/bcryptjs": "^2.4.2", + "@types/fs-extra": "^11.0.1", "@types/jest": "^29.0.3", "@types/supertest": "^2.0.12", "@types/tmp": "^0.2.3", + "@types/uuid": "^8.3.4", + "@zenstackhq/runtime": "workspace:*", + "@zenstackhq/next": "workspace:*", + "@zenstackhq/react": "workspace:*", + "@zenstackhq/trpc": "workspace:*", + "eslint": "^8.30.0", + "eslint-plugin-jest": "^27.1.7", + "fs-extra": "^11.1.0", "jest": "^29.0.3", "jest-fetch-mock": "^3.0.3", "next": "^12.3.1", - "supertest": "^6.3.0", + "prisma": "~4.7.0", "tmp": "^0.2.1", "ts-jest": "^29.0.1", "ts-node": "^10.9.1", - "typescript": "^4.6.2" + "typescript": "^4.6.2", + "uuid": "^9.0.0", + "zenstack": "workspace: *" }, "dependencies": { + "@prisma/client": "^4.7.0", "@types/node": "^14.18.29", "bcryptjs": "^2.4.3", "decimal.js": "^10.4.2", diff --git a/tests/integration/test-run/package-lock.json b/tests/integration/test-run/package-lock.json new file mode 100644 index 000000000..17a748a13 --- /dev/null +++ b/tests/integration/test-run/package-lock.json @@ -0,0 +1,459 @@ +{ + "name": "test-run", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-run", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@prisma/client": "^4.7.1", + "@zenstackhq/runtime": "file:../../../packages/runtime/dist", + "prisma": "~4.7.0", + "react": "^18.2.0", + "swr": "^1.3.0", + "typescript": "^4.9.3", + "zenstack": "file:../../../packages/schema/dist", + "zod": "^3.19.1" + } + }, + "../../../../packages/runtime/dist": { + "name": "@zenstackhq/runtime", + "version": "0.6.0-pre.11", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@types/bcryptjs": "^2.4.2", + "bcryptjs": "^2.4.3", + "change-case": "^4.1.2", + "colors": "1.4.0", + "cuid": "^2.1.8", + "decimal.js": "^10.4.2", + "deepcopy": "^2.1.0", + "superjson": "^1.11.0", + "swr": "^1.3.0", + "tslib": "^2.4.1", + "zod": "^3.19.1", + "zod-validation-error": "^0.2.1" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/jest": "^29.0.3", + "@types/node": "^14.18.29", + "rimraf": "^3.0.2", + "typescript": "^4.9.3" + }, + "peerDependencies": { + "@prisma/client": "^4.7.1", + "next": "^12.3.1 || ^13", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + } + }, + "../../../../packages/schema/dist": { + "name": "zenstack", + "version": "0.6.0-pre.11", + "extraneous": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@prisma/generator-helper": "^4.7.1", + "@prisma/internals": "^4.7.1", + "@zenstackhq/language": "workspace:*", + "@zenstackhq/runtime": "workspace:*", + "@zenstackhq/sdk": "workspace:*", + "async-exit-hook": "^2.0.1", + "change-case": "^4.1.2", + "chevrotain": "^9.1.0", + "colors": "1.4.0", + "commander": "^8.3.0", + "cuid": "^2.1.8", + "langium": "^0.5.0", + "mixpanel": "^0.17.0", + "node-machine-id": "^1.1.12", + "ora": "^5.4.1", + "pluralize": "^8.0.0", + "prisma": "~4.7.0", + "promisify": "^0.0.3", + "sleep-promise": "^9.1.0", + "ts-morph": "^16.0.0", + "uuid": "^9.0.0", + "vscode-jsonrpc": "^8.0.2", + "vscode-languageclient": "^8.0.2", + "vscode-languageserver": "^8.0.2", + "vscode-languageserver-textdocument": "^1.0.7", + "vscode-uri": "^3.0.6", + "zod": "^3.19.1" + }, + "bin": { + "zenstack": "bin/cli" + }, + "devDependencies": { + "@babel/cli": "^7.19.3", + "@babel/core": "^7.20.5", + "@babel/preset-env": "^7.20.2", + "@babel/preset-typescript": "^7.18.6", + "@types/async-exit-hook": "^2.0.0", + "@types/jest": "^29.2.0", + "@types/node": "^14.18.32", + "@types/pluralize": "^0.0.29", + "@types/tmp": "^0.2.3", + "@types/uuid": "^8.3.4", + "@types/vscode": "^1.56.0", + "@typescript-eslint/eslint-plugin": "^5.42.0", + "@typescript-eslint/parser": "^5.42.0", + "babel-plugin-inline-dotenv": "^1.7.0", + "concurrently": "^7.4.0", + "dotenv": "^16.0.3", + "esbuild": "^0.15.12", + "eslint": "^8.27.0", + "jest": "^29.2.1", + "langium-cli": "^0.5.0", + "rimraf": "^3.0.2", + "tmp": "^0.2.1", + "ts-jest": "^29.0.3", + "ts-node": "^10.9.1", + "tsc-alias": "^1.7.0", + "tsconfig-paths-jest": "^0.0.1", + "typescript": "^4.8.4", + "vsce": "^2.13.0" + }, + "engines": { + "vscode": "^1.56.0" + } + }, + "../../../packages/runtime/dist": { + "name": "@zenstackhq/runtime", + "version": "1.0.0-alpha.25", + "license": "MIT", + "dependencies": { + "@types/bcryptjs": "^2.4.2", + "@zenstackhq/sdk": "workspace:*", + "bcryptjs": "^2.4.3", + "change-case": "^4.1.2", + "colors": "1.4.0", + "cuid": "^2.1.8", + "decimal.js": "^10.4.2", + "deepcopy": "^2.1.0", + "superjson": "^1.11.0", + "swr": "^1.3.0", + "tslib": "^2.4.1", + "zod": "^3.19.1", + "zod-validation-error": "^0.2.1" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/jest": "^29.0.3", + "@types/node": "^14.18.29", + "copyfiles": "^2.4.1", + "rimraf": "^3.0.2", + "typescript": "^4.9.3" + }, + "peerDependencies": { + "@prisma/client": "^4.0.0", + "next": "^12.3.1 || ^13", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + } + }, + "../../../packages/schema/dist": { + "name": "zenstack", + "version": "1.0.0-alpha.25", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@prisma/generator-helper": "^4.7.1", + "@prisma/internals": "^4.7.1", + "@zenstackhq/language": "workspace:*", + "@zenstackhq/runtime": "workspace:*", + "@zenstackhq/sdk": "workspace:*", + "async-exit-hook": "^2.0.1", + "change-case": "^4.1.2", + "chevrotain": "^9.1.0", + "colors": "1.4.0", + "commander": "^8.3.0", + "cuid": "^2.1.8", + "langium": "^1.0.1", + "mixpanel": "^0.17.0", + "node-machine-id": "^1.1.12", + "ora": "^5.4.1", + "pluralize": "^8.0.0", + "prisma": "~4.7.0", + "promisify": "^0.0.3", + "semver": "^7.3.8", + "sleep-promise": "^9.1.0", + "ts-morph": "^16.0.0", + "uuid": "^9.0.0", + "vscode-jsonrpc": "^8.0.2", + "vscode-languageclient": "^8.0.2", + "vscode-languageserver": "^8.0.2", + "vscode-languageserver-textdocument": "^1.0.7", + "vscode-uri": "^3.0.6", + "zod": "^3.19.1" + }, + "bin": { + "zenstack": "bin/cli" + }, + "devDependencies": { + "@types/async-exit-hook": "^2.0.0", + "@types/jest": "^29.2.0", + "@types/node": "^14.18.32", + "@types/pluralize": "^0.0.29", + "@types/semver": "^7.3.13", + "@types/tmp": "^0.2.3", + "@types/uuid": "^8.3.4", + "@types/vscode": "^1.56.0", + "@typescript-eslint/eslint-plugin": "^5.42.0", + "@typescript-eslint/parser": "^5.42.0", + "concurrently": "^7.4.0", + "copyfiles": "^2.4.1", + "dotenv": "^16.0.3", + "esbuild": "^0.15.12", + "eslint": "^8.27.0", + "eslint-plugin-jest": "^27.1.7", + "jest": "^29.2.1", + "langium-cli": "^1.0.0", + "renamer": "^4.0.0", + "rimraf": "^3.0.2", + "tmp": "^0.2.1", + "ts-jest": "^29.0.3", + "ts-node": "^10.9.1", + "tsc-alias": "^1.7.0", + "typescript": "^4.8.4", + "vsce": "^2.13.0" + }, + "engines": { + "vscode": "^1.56.0" + } + }, + "node_modules/@prisma/client": { + "version": "4.7.1", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines": { + "version": "4.7.1", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines-version": { + "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c", + "license": "Apache-2.0" + }, + "node_modules/@zenstackhq/runtime": { + "resolved": "../../../packages/runtime/dist", + "link": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "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/prisma": { + "version": "4.7.1", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "4.7.1" + }, + "bin": { + "prisma": "build/index.js", + "prisma2": "build/index.js" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/react": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/swr": { + "version": "1.3.0", + "license": "MIT", + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.3", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/zenstack": { + "resolved": "../../../packages/schema/dist", + "link": true + }, + "node_modules/zod": { + "version": "3.19.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + }, + "dependencies": { + "@prisma/client": { + "version": "4.7.1", + "requires": { + "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + } + }, + "@prisma/engines": { + "version": "4.7.1" + }, + "@prisma/engines-version": { + "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + }, + "@zenstackhq/runtime": { + "version": "file:../../../packages/runtime/dist", + "requires": { + "@types/bcryptjs": "^2.4.2", + "@types/jest": "^29.0.3", + "@types/node": "^14.18.29", + "@zenstackhq/sdk": "workspace:*", + "bcryptjs": "^2.4.3", + "change-case": "^4.1.2", + "colors": "1.4.0", + "copyfiles": "^2.4.1", + "cuid": "^2.1.8", + "decimal.js": "^10.4.2", + "deepcopy": "^2.1.0", + "rimraf": "^3.0.2", + "superjson": "^1.11.0", + "swr": "^1.3.0", + "tslib": "^2.4.1", + "typescript": "^4.9.3", + "zod": "^3.19.1", + "zod-validation-error": "^0.2.1" + } + }, + "js-tokens": { + "version": "4.0.0" + }, + "loose-envify": { + "version": "1.4.0", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "prisma": { + "version": "4.7.1", + "requires": { + "@prisma/engines": "4.7.1" + } + }, + "react": { + "version": "18.2.0", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "swr": { + "version": "1.3.0", + "requires": {} + }, + "typescript": { + "version": "4.9.3" + }, + "zenstack": { + "version": "file:../../../packages/schema/dist", + "requires": { + "@prisma/generator-helper": "^4.7.1", + "@prisma/internals": "^4.7.1", + "@types/async-exit-hook": "^2.0.0", + "@types/jest": "^29.2.0", + "@types/node": "^14.18.32", + "@types/pluralize": "^0.0.29", + "@types/semver": "^7.3.13", + "@types/tmp": "^0.2.3", + "@types/uuid": "^8.3.4", + "@types/vscode": "^1.56.0", + "@typescript-eslint/eslint-plugin": "^5.42.0", + "@typescript-eslint/parser": "^5.42.0", + "@zenstackhq/language": "workspace:*", + "@zenstackhq/runtime": "workspace:*", + "@zenstackhq/sdk": "workspace:*", + "async-exit-hook": "^2.0.1", + "change-case": "^4.1.2", + "chevrotain": "^9.1.0", + "colors": "1.4.0", + "commander": "^8.3.0", + "concurrently": "^7.4.0", + "copyfiles": "^2.4.1", + "cuid": "^2.1.8", + "dotenv": "^16.0.3", + "esbuild": "^0.15.12", + "eslint": "^8.27.0", + "eslint-plugin-jest": "^27.1.7", + "jest": "^29.2.1", + "langium": "^1.0.1", + "langium-cli": "^1.0.0", + "mixpanel": "^0.17.0", + "node-machine-id": "^1.1.12", + "ora": "^5.4.1", + "pluralize": "^8.0.0", + "prisma": "~4.7.0", + "promisify": "^0.0.3", + "renamer": "^4.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.8", + "sleep-promise": "^9.1.0", + "tmp": "^0.2.1", + "ts-jest": "^29.0.3", + "ts-morph": "^16.0.0", + "ts-node": "^10.9.1", + "tsc-alias": "^1.7.0", + "typescript": "^4.8.4", + "uuid": "^9.0.0", + "vsce": "^2.13.0", + "vscode-jsonrpc": "^8.0.2", + "vscode-languageclient": "^8.0.2", + "vscode-languageserver": "^8.0.2", + "vscode-languageserver-textdocument": "^1.0.7", + "vscode-uri": "^3.0.6", + "zod": "^3.19.1" + } + }, + "zod": { + "version": "3.19.1" + } + } +} diff --git a/tests/integration/test-run/package.json b/tests/integration/test-run/package.json new file mode 100644 index 000000000..1c7963cbd --- /dev/null +++ b/tests/integration/test-run/package.json @@ -0,0 +1,22 @@ +{ + "name": "test-run", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@prisma/client": "^4.7.1", + "@zenstackhq/runtime": "file:../../../packages/runtime/dist", + "prisma": "~4.7.0", + "react": "^18.2.0", + "swr": "^1.3.0", + "typescript": "^4.9.3", + "zenstack": "file:../../../packages/schema/dist", + "zod": "^3.19.1" + } +} diff --git a/tests/integration/test-setup.ts b/tests/integration/test-setup.ts new file mode 100644 index 000000000..6936a0cd4 --- /dev/null +++ b/tests/integration/test-setup.ts @@ -0,0 +1,17 @@ +import { + toBeRejectedByPolicy, + toBeNotFound, + toResolveTruthy, + toResolveFalsy, + toResolveNull, + toBeRejectedWithCode, +} from './utils/jest-ext'; + +expect.extend({ + toBeRejectedByPolicy, + toBeNotFound, + toResolveTruthy, + toResolveFalsy, + toResolveNull, + toBeRejectedWithCode, +}); diff --git a/tests/integration/tests/e2e/prisma-methods.test.ts b/tests/integration/tests/e2e/prisma-methods.test.ts new file mode 100644 index 000000000..2cae859d9 --- /dev/null +++ b/tests/integration/tests/e2e/prisma-methods.test.ts @@ -0,0 +1,134 @@ +import { AuthUser } from '@zenstackhq/runtime'; +import { loadPrisma, MODEL_PRELUDE, run, WeakDbClientContract } from '../../utils'; + +describe('Prisma Methods Tests', () => { + let getDb: (user?: AuthUser) => WeakDbClientContract; + let prisma: WeakDbClientContract; + + beforeAll(async () => { + const { withPresets, prisma: _prisma } = await loadPrisma( + 'prisma-methods', + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(cuid()) + value Int + + @@allow('all', value > 0) + } + ` + ); + getDb = withPresets; + prisma = _prisma; + }); + + beforeEach(() => { + run('npx prisma migrate reset --force'); + run('npx prisma db push'); + }); + + it('transaction', async () => { + const db = getDb({ id: 'user1' }); + + const r = await db.$transaction(async (tx) => { + const r1 = await tx.model.create({ data: { id: '1', value: 1 } }); + const r2 = await tx.model.create({ data: { id: '2', value: 2 } }); + return [r1, r2]; + }); + + expect(r).toEqual( + expect.arrayContaining([expect.objectContaining({ id: '1' }), expect.objectContaining({ id: '2' })]) + ); + + await expect( + db.$transaction(async (tx) => { + const r3 = await tx.model.create({ data: { id: '3', value: 3 } }); + const r4 = await tx.model.create({ data: { id: '4', value: -1 } }); + return [r3, r4]; + }) + ).rejects.toThrow(); + + const r3 = await db.model.findUnique({ where: { id: '3' } }); + expect(r3).toBeNull(); + const r4 = await db.model.findUnique({ where: { id: '4' } }); + expect(r4).toBeNull(); + }); + + it('raw sql', async () => { + const db = getDb(); + + const r = await db.$transaction(async (tx) => { + const r1 = await tx.model.create({ data: { id: '1', value: 1 } }); + const r2 = await tx.model.create({ data: { id: '2', value: 2 } }); + return [r1, r2]; + }); + + const q = await db.$queryRaw`SELECT * FROM "Model" WHERE id=${r[1].id};`; + expect(q[0]).toEqual(expect.objectContaining(r[1])); + + const r1 = await db.$executeRaw`UPDATE "Model" SET value=5 WHERE id="1";`; + expect(r1).toBe(1); + await expect(db.model.findUnique({ where: { id: '1' } })).resolves.toEqual( + expect.objectContaining({ value: 5 }) + ); + }); + + it('middleware', async () => { + const db = getDb(); + let middlewareCalled = false; + db.$use(async (params: any, next: Function) => { + middlewareCalled = true; + return next(params); + }); + + await db.model.create({ data: { id: '1', value: 1 } }); + expect(middlewareCalled).toBeTruthy(); + }); + + it('extension', async () => { + const db = getDb(); + + const extendedDb: any = db.$extends({ + client: { + $hello: (name: string) => `Hello ${name}`, + }, + query: { + model: { + findMany({ query, args }: { query: Function; args: any }) { + args.where = { value: { gt: 1 }, ...args.where }; + return query(args); + }, + }, + }, + model: { + model: { + greet: (name: string) => `Greeting from ${name}`, + }, + }, + result: { + model: { + valuePlus: { + needs: { value: true }, + compute(model: any) { + return model.value + 1; + }, + }, + }, + }, + }); + + expect(extendedDb.$hello('world')).toBe('Hello world'); + + await db.model.create({ data: { id: '1', value: 1 } }); + await db.model.create({ data: { id: '2', value: 2 } }); + await expect(db.model.findMany()).resolves.toHaveLength(2); + + const r = await extendedDb.model.findMany(); + expect(r).toHaveLength(1); + expect(r[0].value).toBe(2); + expect(r[0].valuePlus).toBe(3); + + expect(extendedDb.model.greet('ymc9')).toBe('Greeting from ymc9'); + }); +}); diff --git a/tests/integration/tests/e2e/todo-presets.test.ts b/tests/integration/tests/e2e/todo-presets.test.ts new file mode 100644 index 000000000..953ea0402 --- /dev/null +++ b/tests/integration/tests/e2e/todo-presets.test.ts @@ -0,0 +1,37 @@ +import { AuthUser } from '@zenstackhq/runtime'; +import { compareSync } from 'bcryptjs'; +import path from 'path'; +import { WeakDbClientContract, loadPrismaFromModelFile, run } from '../../utils'; + +describe('Todo Presets Tests', () => { + let getDb: (user?: AuthUser) => WeakDbClientContract; + let prisma: WeakDbClientContract; + + beforeAll(async () => { + const { withPresets, prisma: _prisma } = await loadPrismaFromModelFile( + 'todo-presets', + path.join(__dirname, '../schema/todo.zmodel') + ); + getDb = withPresets; + prisma = _prisma; + }); + + beforeEach(() => { + run('npx prisma migrate reset --force'); + run('npx prisma db push'); + }); + + it('user', async () => { + const anonDb = getDb(); + const user1Db = getDb({ id: 'user1' }); + + await expect(anonDb.user.create({ data: { email: 'abc.xyz' } })).toBeRejectedByPolicy(['Invalid email']); + + const r = await user1Db.user.create({ data: { id: 'user1', email: 'abc@xyz.com', password: 'abc123' } }); + expect(r.password).toBeUndefined(); + const full = await prisma.user.findUnique({ where: { id: 'user1' } }); + expect(compareSync('abc123', full.password)).toBe(true); + + await expect(anonDb.user.findUnique({ where: { id: 'user1' } })).toResolveNull(); + }); +}); diff --git a/tests/integration/tests/e2e/type-coverage.test.ts b/tests/integration/tests/e2e/type-coverage.test.ts new file mode 100644 index 000000000..856a639cf --- /dev/null +++ b/tests/integration/tests/e2e/type-coverage.test.ts @@ -0,0 +1,64 @@ +import { AuthUser } from '@zenstackhq/runtime'; +import Decimal from 'decimal.js'; +import superjson from 'superjson'; +import { loadPrisma, MODEL_PRELUDE, run, WeakDbClientContract } from '../../utils'; + +describe('Type Coverage Tests', () => { + let getDb: (user?: AuthUser) => WeakDbClientContract; + let prisma: WeakDbClientContract; + + beforeAll(async () => { + const { withPresets, prisma: _prisma } = await loadPrisma( + 'type-coverate', + ` + ${MODEL_PRELUDE} + + model Foo { + id String @id @default(cuid()) + + string String + int Int + bigInt BigInt + date DateTime + float Float + decimal Decimal + boolean Boolean + bytes Bytes + + @@allow('all', true) + } + ` + ); + getDb = withPresets; + prisma = _prisma; + }); + + beforeEach(() => { + run('npx prisma migrate reset --force'); + run('npx prisma db push'); + }); + + it('coverage', async () => { + const db = getDb(); + + const date = new Date(); + const data = { + id: '1', + string: 'string', + int: 100, + bigInt: BigInt(9007199254740991), + date, + float: 1.23, + decimal: new Decimal(1.2345), + boolean: true, + bytes: Buffer.from('hello'), + }; + + await db.foo.create({ + data, + }); + + const r = await db.foo.findUnique({ where: { id: '1' } }); + expect(superjson.stringify(r)).toEqual(superjson.stringify(data)); + }); +}); diff --git a/tests/integration/tests/field-validation-client.test.ts b/tests/integration/tests/field-validation-client.test.ts deleted file mode 100644 index 9f625f9fc..000000000 --- a/tests/integration/tests/field-validation-client.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -import path from 'path'; -import { run, setup } from './utils'; -import { default as fetch, enableFetchMocks } from 'jest-fetch-mock'; - -describe('Field validation client-side tests', () => { - let origDir: string; - - const hooksModule = '@zenstackhq/runtime/client'; - const requestModule = '@zenstackhq/runtime/lib/request'; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/field-validation.zmodel'); - - // mock mutate method - jest.mock(requestModule, () => ({ - ...jest.requireActual(requestModule), - getMutate: jest.fn(() => jest.fn()), - })); - - // mock fetch - enableFetchMocks(); - fetch.mockResponse(JSON.stringify({ status: 'ok' })); - }); - - beforeEach(async () => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - jest.resetAllMocks(); - }); - - async function expectErrors( - call: () => Promise, - expectedErrors: string[] - ) { - try { - await call(); - } catch (err: any) { - if (!err.message) { - throw err; - } - const errors: string[] = err.message.split(';'); - expect(errors).toEqual( - expect.arrayContaining( - expectedErrors.map((e) => expect.stringContaining(e)) - ) - ); - return; - } - - throw new Error('Error is expected'); - } - - it('direct write test', async () => { - const { useUser } = await import(hooksModule); - const { create: createUser, update: updateUser } = useUser(); - - expectErrors( - () => - createUser({ - data: { - id: '1', - password: 'abc123', - handle: 'hello world', - }, - }), - ['password', 'email', 'handle'] - ); - - await createUser({ - data: { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }, - }); - - expectErrors( - () => - updateUser('1', { - data: { - password: 'abc123', - email: 'me@test.org', - handle: 'hello world', - }, - }), - ['password', 'email', 'handle'] - ); - - await updateUser('1', { - data: { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }, - }); - }); - - it('nested write test', async () => { - const { useUser } = await import(hooksModule); - const { create: createUser, update: updateUser } = useUser(); - - const userData = { - a: 1, - b: 0, - c: -1, - d: 0, - text1: 'abc123', - text2: 'def', - text3: 'aaa', - text4: 'abcab', - }; - - const tasks = [ - { - slug: 'abcabc', - }, - { - slug: 'abcdef', - }, - ]; - - expectErrors( - () => - createUser({ - data: { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - - userData: { - create: { - a: 0, - }, - }, - - tasks: { - create: { - slug: 'xyz', - }, - }, - }, - }), - ['userData.create', 'tasks.create.slug'] - ); - - await createUser({ - data: { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - - userData: { - create: userData, - }, - - tasks: { - create: tasks, - }, - }, - }); - - expectErrors( - () => - updateUser('1', { - data: { - userData: { - update: { - a: 0, - }, - }, - - tasks: { - update: { - where: { id: 1 }, - data: { - slug: 'xyz', - }, - }, - }, - }, - }), - ['userData.update', 'tasks.update.data.slug'] - ); - - await updateUser('1', { - data: { - userData: { - update: { - a: 1, - }, - }, - - tasks: { - update: { - where: { id: 1 }, - data: { - slug: 'abcxyz', - }, - }, - }, - }, - }); - }); -}); diff --git a/tests/integration/tests/field-validation-server.test.ts b/tests/integration/tests/field-validation-server.test.ts deleted file mode 100644 index e39f37b74..000000000 --- a/tests/integration/tests/field-validation-server.test.ts +++ /dev/null @@ -1,548 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import { ServerErrorCode } from '../../../packages/runtime/src/types'; - -describe('Field validation server-side tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/field-validation.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - it('direct write test', async () => { - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123', - handle: 'hello world', - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'String must contain at least 8 character(s) at "password"' - ); - expect(resp.body.message).toContain('Required at "email"'); - expect(resp.body.message).toContain('Invalid at "handle"'); - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123!@#', - email: 'something', - handle: 'user1user1user1user1user1', - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain('Invalid email at "email"'); - expect(resp.body.message).toContain( - 'must end with "@myorg.com" at "email"' - ); - expect(resp.body.message).toContain('Invalid at "handle"'); - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }, - }) - .expect(201); - - await makeClient('/api/data/User/1') - .put('/') - .send({ - data: { - password: 'abc123', - email: 'something', - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'String must contain at least 8 character(s) at "password"' - ); - expect(resp.body.message).toContain('Invalid email at "email"'); - expect(resp.body.message).toContain( - 'must end with "@myorg.com" at "email"' - ); - }); - }); - - it('direct write more test', async () => { - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }, - }) - .expect(201); - - await makeClient('/api/data/UserData') - .post('/') - .send({ - data: { - userId: '1', - a: 0, - b: -1, - c: 0, - d: 1, - text1: 'a', - text2: 'xyz', - text3: 'a', - text4: 'abcabc', - text5: 'abc', - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Number must be greater than 0 at "a"' - ); - expect(resp.body.message).toContain( - 'Number must be greater than or equal to 0 at "b"' - ); - expect(resp.body.message).toContain( - 'Number must be less than 0 at "c"' - ); - expect(resp.body.message).toContain( - 'Number must be less than or equal to 0 at "d"' - ); - expect(resp.body.message).toContain( - 'must start with "abc" at "text1"' - ); - expect(resp.body.message).toContain( - 'must end with "def" at "text2"' - ); - expect(resp.body.message).toContain( - 'String must contain at least 3 character(s) at "text3"' - ); - expect(resp.body.message).toContain( - 'String must contain at most 5 character(s) at "text4"' - ); - expect(resp.body.message).toContain( - 'must end with "xyz" at "text5"' - ); - }); - - await makeClient('/api/data/UserData') - .post('/') - .send({ - data: { - userId: '1', - a: 1, - b: 0, - c: -1, - d: 0, - text1: 'abc123', - text2: 'def', - text3: 'aaa', - text4: 'abcab', - }, - }) - .expect(201); - }); - - it('nested create test', async () => { - const user = { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }; - - const userData = { - a: 1, - b: 0, - c: -1, - d: 0, - text1: 'abc123', - text2: 'def', - text3: 'aaa', - text4: 'abcab', - }; - - const tasks = [ - { - slug: 'abcabc', - }, - { - slug: 'abcdef', - }, - ]; - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - ...user, - userData: { - create: { - a: 0, - }, - }, - tasks: { - create: { - slug: 'abc', - }, - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Invalid input at "userData.create"' - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.create.slug"' - ); - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - ...user, - userData: { create: userData }, - tasks: { - create: { - slug: 'abcabc', - }, - }, - }, - }) - .expect(201); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - ...user, - userData: { - connectOrCreate: { - where: { - id: '1', - }, - create: { - a: 0, - }, - }, - }, - tasks: { - create: [ - { - slug: 'abc', - }, - { - slug: 'abcdef', - }, - ], - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Invalid input at "userData.connectOrCreate"' - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.create[0].slug"' - ); - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - ...user, - tasks: { - createMany: [ - { - slug: 'abc', - }, - { - slug: 'abcdef', - }, - ], - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.createMany[0].slug"' - ); - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - ...user, - userData: { - connectOrCreate: { - where: { - id: '1', - }, - create: userData, - }, - }, - tasks: { - create: tasks, - }, - }, - }) - .expect(201); - }); - - it('nested update test', async () => { - const user = { - password: 'abc123!@#', - email: 'who@myorg.com', - handle: 'user1', - }; - - const userData = { - a: 1, - b: 0, - c: -1, - d: 0, - text1: 'abc123', - text2: 'def', - text3: 'aaa', - text4: 'abcab', - }; - - const tasks = [ - { - id: '1', - slug: 'abcabc', - }, - { - id: '2', - slug: 'abcdef', - }, - ]; - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - ...user, - }, - }) - .expect(201); - - const client = makeClient('/api/data/User/1'); - - await client - .put('/') - .send({ - data: { - userData: { - create: { - a: 0, - }, - }, - tasks: { - create: { - slug: 'abc', - }, - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Invalid input at "userData.create"' - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.create.slug"' - ); - }); - - await client - .put('/') - .send({ - data: { - userData: { - create: { id: '1', ...userData }, - }, - tasks: { - create: { - id: '1', - slug: 'abcabc', - }, - }, - }, - }) - .expect(200); - - await client - .put('/') - .send({ - data: { - userData: { - update: { - a: 0, - }, - }, - tasks: { - update: { - where: { id: '1' }, - data: { - slug: 'abc', - }, - }, - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Number must be greater than 0 at "userData.update.a"' - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.update.data.slug"' - ); - }); - - await client - .put('/') - .send({ - data: { - userData: { - update: { - a: 2, - }, - }, - tasks: { - update: { - where: { id: '1' }, - data: { - slug: 'defdef', - }, - }, - }, - }, - }) - .expect(200); - - await client - .put('/') - .send({ - where: { id: '1' }, - data: { - userData: { - upsert: { - create: { - a: 0, - }, - update: { - a: 0, - }, - }, - }, - tasks: { - updateMany: { - where: { id: '1' }, - data: { - slug: 'abc', - }, - }, - }, - }, - }) - .expect(400) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.INVALID_REQUEST_PARAMS - ); - expect(resp.body.message).toContain( - 'Number must be greater than 0 at "userData.upsert.create.a"' - ); - expect(resp.body.message).toContain( - 'Number must be greater than 0 at "userData.upsert.update.a"' - ); - expect(resp.body.message).toContain( - 'Invalid at "tasks.updateMany.data.slug"' - ); - }); - - await client - .put('/') - .send({ - data: { - userData: { - upsert: { - create: { - ...userData, - }, - update: { - a: 1, - }, - }, - }, - tasks: { - updateMany: { - where: { id: '1' }, - data: { - slug: 'xxxyyy', - }, - }, - }, - }, - }) - .expect(200); - }); -}); diff --git a/tests/integration/tests/field-validation.zmodel b/tests/integration/tests/field-validation.zmodel deleted file mode 100644 index f46b5be90..000000000 --- a/tests/integration/tests/field-validation.zmodel +++ /dev/null @@ -1,44 +0,0 @@ -datasource db { - provider = 'sqlite' - url = 'file:./field-validation.db' -} - -model User { - id String @id @default(cuid()) - password String @length(8, 16) - email String @email @endsWith("@myorg.com") - profileImage String? @url - handle String @regex("^[0-9a-zA-Z]{4,16}$") - - userData UserData? - tasks Task[] - - @@allow('all', true) -} - -model UserData { - id String @id @default(cuid()) - user User @relation(fields: [userId], references: [id]) - userId String @unique - - a Int @gt(0) - b Int @gte(0) - c Int @lt(0) - d Int @lte(0) - text1 String @startsWith('abc') - text2 String @endsWith('def') - text3 String @length(min: 3) - text4 String @length(max: 5) - text5 String? @endsWith('xyz') - - @@allow('all', true) -} - -model Task { - id String @id @default(cuid()) - user User @relation(fields: [userId], references: [id]) - userId String - slug String @regex("^[0-9a-zA-Z]{4,16}$") - - @@allow('all', true) -} diff --git a/tests/integration/tests/logging.test.ts b/tests/integration/tests/logging.test.ts deleted file mode 100644 index d122efd29..000000000 --- a/tests/integration/tests/logging.test.ts +++ /dev/null @@ -1,266 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import * as fs from 'fs'; -import type { DefaultService } from '../../../packages/runtime/src/service'; - -describe('Logging tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/todo.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - const getService = () => require('@zenstackhq/runtime/server').default; - - it('logging with default settings', async () => { - const service: DefaultService = getService(); - service.reinitialize(); - - let gotInfoEmit = false; - let gotQueryEmit = false; - let gotVerboseEmit = false; - let gotWarnEmit = false; - - let gotInfoStd = false; - let gotQueryStd = false; - let gotVerboseStd = false; - let gotWarnStd = false; - - console.log = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes('prisma:query')) { - gotQueryStd = true; - } - if (msg.includes(':verbose')) { - gotVerboseStd = true; - } - if (msg.includes(':info')) { - gotInfoStd = true; - } - }); - - console.warn = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes(':warn')) { - gotWarnStd = true; - } - }); - - service.$on('info', (event) => { - console.log('Got info', event); - gotInfoEmit = true; - }); - - service.$on('query', (event) => { - console.log('Got query', event); - gotQueryEmit = true; - }); - - service.$on('verbose', (event) => { - console.log('Got verbose', event); - gotVerboseEmit = true; - }); - - service.$on('warn', (event) => { - console.log('Got warn', event); - gotWarnEmit = true; - }); - - await makeClient('/api/data/User').post('/').send({ - data: {}, - }); - - expect(gotQueryStd).toBeFalsy(); - expect(gotVerboseStd).toBeFalsy(); - expect(gotInfoStd).toBeFalsy(); - expect(gotWarnStd).toBeTruthy(); - - expect(gotInfoEmit).toBeFalsy(); - expect(gotQueryEmit).toBeFalsy(); - expect(gotVerboseEmit).toBeFalsy(); - expect(gotWarnEmit).toBeFalsy(); - }); - - it('logging with stdout', async () => { - fs.writeFileSync( - './zenstack.config.json', - ` - { - "log": ["query", "verbose", "info", "warn"] - } - ` - ); - - const service: DefaultService = getService(); - service.reinitialize(); - - let gotInfoEmit = false; - let gotQueryEmit = false; - let gotVerboseEmit = false; - let gotWarnEmit = false; - - let gotInfoStd = false; - let gotQueryStd = false; - let gotVerboseStd = false; - let gotWarnStd = false; - - console.log = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes(':query')) { - gotQueryStd = true; - } - if (msg.includes(':verbose')) { - gotVerboseStd = true; - } - if (msg.includes(':info')) { - gotInfoStd = true; - } - }); - - console.warn = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes(':warn')) { - gotWarnStd = true; - } - }); - - service.$on('info', (event) => { - console.log('Got info', event); - gotInfoEmit = true; - }); - - service.$on('query', (event) => { - console.log('Got query', event); - gotQueryEmit = true; - }); - - service.$on('verbose', (event) => { - console.log('Got verbose', event); - gotVerboseEmit = true; - }); - - service.$on('warn', (event) => { - console.log('Got warn', event); - gotWarnEmit = true; - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - email: 'abc@def.com', - }, - }); - - expect(gotQueryStd).toBeTruthy(); - expect(gotVerboseStd).toBeTruthy(); - expect(gotInfoStd).toBeTruthy(); - expect(gotWarnStd).toBeTruthy(); - - expect(gotInfoEmit).toBeFalsy(); - expect(gotQueryEmit).toBeFalsy(); - expect(gotVerboseEmit).toBeFalsy(); - expect(gotWarnEmit).toBeFalsy(); - }); - - it('logging with event', async () => { - fs.writeFileSync( - './zenstack.config.json', - ` - { - "log": [ - { "level": "query", "emit": "event" }, - { "level": "verbose", "emit": "event" }, - { "level": "info", "emit": "event" }, - { "level": "warn", "emit": "event" } - ] - } - ` - ); - - const service: DefaultService = getService(); - service.reinitialize(); - - let gotInfoEmit = false; - let gotQueryEmit = false; - let gotVerboseEmit = false; - let gotWarnEmit = false; - - let gotInfoStd = false; - let gotQueryStd = false; - let gotVerboseStd = false; - let gotWarnStd = false; - - console.log = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes(':query')) { - gotQueryStd = true; - } - if (msg.includes(':verbose')) { - gotVerboseStd = true; - } - if (msg.includes(':info')) { - gotInfoStd = true; - } - }); - - console.warn = jest.fn((...args) => { - const msg = args?.[0] as string; - if (msg.includes('zenstack:warn')) { - gotWarnStd = true; - } - }); - - service.$on('info', (event) => { - expect(event.timestamp).toBeTruthy(); - expect(event.message).toBeTruthy(); - gotInfoEmit = true; - }); - - service.$on('query', (event) => { - expect(event.timestamp).not.toBeUndefined(); - expect(event.query).not.toBeUndefined(); - expect(event.duration).not.toBeUndefined(); - gotQueryEmit = true; - }); - - service.$on('verbose', (event) => { - expect(event.timestamp).not.toBeUndefined(); - expect(event.message).not.toBeUndefined(); - gotVerboseEmit = true; - }); - - service.$on('warn', (event) => { - expect(event.timestamp).not.toBeUndefined(); - expect(event.message).not.toBeUndefined(); - gotWarnEmit = true; - }); - - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - email: 'abc@def.com', - }, - }); - - expect(gotInfoEmit).toBeTruthy(); - expect(gotQueryEmit).toBeTruthy(); - expect(gotVerboseEmit).toBeTruthy(); - expect(gotWarnEmit).toBeTruthy(); - - expect(gotInfoStd).toBeFalsy(); - expect(gotQueryStd).toBeFalsy(); - expect(gotVerboseStd).toBeFalsy(); - expect(gotWarnStd).toBeFalsy(); - }); -}); diff --git a/tests/integration/tests/nextjs/generation.test.ts b/tests/integration/tests/nextjs/generation.test.ts new file mode 100644 index 000000000..435dc6b25 --- /dev/null +++ b/tests/integration/tests/nextjs/generation.test.ts @@ -0,0 +1,48 @@ +import fs from 'fs'; +import fse from 'fs-extra'; +import path from 'path'; +import { run } from '../../utils'; + +describe('React Hooks Generation Tests', () => { + let origDir: string; + + beforeAll(() => { + origDir = process.cwd(); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('sqlite', async () => { + const testDir = path.join(__dirname, './test-run/sqlite'); + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + fs.mkdirSync(testDir, { recursive: true }); + fse.copySync(path.join(__dirname, './test-project'), testDir); + + const nodePath = path.resolve(path.join(__dirname, '../../node_modules')); + + process.chdir(testDir); + run('npm install'); + run('npx zenstack generate --schema ./sqlite.zmodel', { NODE_PATH: nodePath }); + run('npm run build', { NODE_PATH: nodePath }); + }); + + it('postgres', async () => { + const testDir = path.join(__dirname, './test-run/postgres'); + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + fs.mkdirSync(testDir, { recursive: true }); + fse.copySync(path.join(__dirname, './test-project'), testDir); + + const nodePath = path.resolve(path.join(__dirname, '../../node_modules')); + + process.chdir(testDir); + run('npm install'); + run('npx zenstack generate --schema ./postgres.zmodel', { NODE_PATH: nodePath }); + run('npm run build', { NODE_PATH: nodePath }); + }); +}); diff --git a/doc-serve/.gitignore b/tests/integration/tests/nextjs/test-project/.gitignore similarity index 100% rename from doc-serve/.gitignore rename to tests/integration/tests/nextjs/test-project/.gitignore diff --git a/tests/integration/tests/nextjs/test-project/.npmrc b/tests/integration/tests/nextjs/test-project/.npmrc new file mode 100644 index 000000000..e38856e8a --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/.npmrc @@ -0,0 +1 @@ +cache=../../../.npmcache diff --git a/doc-serve/README.md b/tests/integration/tests/nextjs/test-project/README.md similarity index 100% rename from doc-serve/README.md rename to tests/integration/tests/nextjs/test-project/README.md diff --git a/doc-serve/next.config.js b/tests/integration/tests/nextjs/test-project/next.config.js similarity index 100% rename from doc-serve/next.config.js rename to tests/integration/tests/nextjs/test-project/next.config.js diff --git a/tests/integration/tests/nextjs/test-project/package-lock.json b/tests/integration/tests/nextjs/test-project/package-lock.json new file mode 100644 index 000000000..304de992c --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/package-lock.json @@ -0,0 +1,718 @@ +{ + "name": "test-project", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-project", + "version": "0.1.0", + "dependencies": { + "@types/node": "18.11.18", + "@types/react": "18.0.27", + "@types/react-dom": "18.0.10", + "next": "13.1.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "4.9.4" + } + }, + "node_modules/@next/env": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz", + "integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A==" + }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz", + "integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz", + "integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz", + "integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz", + "integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-freebsd-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz", + "integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz", + "integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz", + "integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz", + "integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz", + "integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz", + "integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz", + "integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz", + "integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz", + "integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/react": { + "version": "18.0.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz", + "integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==", + "dependencies": { + "@next/env": "13.1.4", + "@swc/helpers": "0.4.14", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=14.6.0" + }, + "optionalDependencies": { + "@next/swc-android-arm-eabi": "13.1.4", + "@next/swc-android-arm64": "13.1.4", + "@next/swc-darwin-arm64": "13.1.4", + "@next/swc-darwin-x64": "13.1.4", + "@next/swc-freebsd-x64": "13.1.4", + "@next/swc-linux-arm-gnueabihf": "13.1.4", + "@next/swc-linux-arm64-gnu": "13.1.4", + "@next/swc-linux-arm64-musl": "13.1.4", + "@next/swc-linux-x64-gnu": "13.1.4", + "@next/swc-linux-x64-musl": "13.1.4", + "@next/swc-win32-arm64-msvc": "13.1.4", + "@next/swc-win32-ia32-msvc": "13.1.4", + "@next/swc-win32-x64-msvc": "13.1.4" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^6.0.0 || ^7.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "@next/env": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz", + "integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A==" + }, + "@next/swc-android-arm-eabi": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz", + "integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz", + "integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==", + "optional": true + }, + "@next/swc-darwin-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz", + "integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz", + "integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==", + "optional": true + }, + "@next/swc-freebsd-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz", + "integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz", + "integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz", + "integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz", + "integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz", + "integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz", + "integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz", + "integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz", + "integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz", + "integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==", + "optional": true + }, + "@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/react": { + "version": "18.0.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "caniuse-lite": { + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==" + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "next": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz", + "integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==", + "requires": { + "@next/env": "13.1.4", + "@next/swc-android-arm-eabi": "13.1.4", + "@next/swc-android-arm64": "13.1.4", + "@next/swc-darwin-arm64": "13.1.4", + "@next/swc-darwin-x64": "13.1.4", + "@next/swc-freebsd-x64": "13.1.4", + "@next/swc-linux-arm-gnueabihf": "13.1.4", + "@next/swc-linux-arm64-gnu": "13.1.4", + "@next/swc-linux-arm64-musl": "13.1.4", + "@next/swc-linux-x64-gnu": "13.1.4", + "@next/swc-linux-x64-musl": "13.1.4", + "@next/swc-win32-arm64-msvc": "13.1.4", + "@next/swc-win32-ia32-msvc": "13.1.4", + "@next/swc-win32-x64-msvc": "13.1.4", + "@swc/helpers": "0.4.14", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "requires": { + "client-only": "0.0.1" + } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + } + } +} diff --git a/tests/integration/tests/nextjs/test-project/package.json b/tests/integration/tests/nextjs/test-project/package.json new file mode 100644 index 000000000..d57d1d392 --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/package.json @@ -0,0 +1,24 @@ +{ + "name": "test-project", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@types/node": "18.11.18", + "@types/react": "18.0.27", + "@types/react-dom": "18.0.10", + "next": "13.1.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "4.9.4", + "@prisma/client": "^4.7.0" + }, + "devDependencies": { + "prisma": "^4.7.0" + } +} diff --git a/tests/integration/tests/nextjs/test-project/pages/_app.tsx b/tests/integration/tests/nextjs/test-project/pages/_app.tsx new file mode 100644 index 000000000..29ae42c3b --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/pages/_app.tsx @@ -0,0 +1,5 @@ +import type { AppProps } from 'next/app'; + +export default function App({ Component, pageProps }: AppProps) { + return ; +} diff --git a/tests/integration/tests/nextjs/test-project/pages/api/model/[...path].ts b/tests/integration/tests/nextjs/test-project/pages/api/model/[...path].ts new file mode 100644 index 000000000..2cc7bf38d --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/pages/api/model/[...path].ts @@ -0,0 +1,7 @@ +import { requestHandler } from '@zenstackhq/next'; +import { withPresets } from '@zenstackhq/runtime'; +import { prisma } from '../../../server/db'; + +export default requestHandler({ + getPrisma: (req, res) => withPresets(prisma, { user: { id: 'user1' } }), +}); diff --git a/tests/integration/tests/nextjs/test-project/pages/index.tsx b/tests/integration/tests/nextjs/test-project/pages/index.tsx new file mode 100644 index 000000000..d50523aa2 --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/pages/index.tsx @@ -0,0 +1,13 @@ +import { usePost } from '../lib/hooks'; + +export default function Home() { + const { findMany } = usePost(); + const { data: posts } = findMany(); + return ( +

+ {posts?.map((post) => ( +

{post.title}

+ ))} +
+ ); +} diff --git a/tests/integration/tests/nextjs/test-project/postgres.zmodel b/tests/integration/tests/nextjs/test-project/postgres.zmodel new file mode 100644 index 000000000..5ca059107 --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/postgres.zmodel @@ -0,0 +1,33 @@ +datasource db { + provider = 'postgresql' + url = env('DATABASE_URL') +} + +generator js { + provider = 'prisma-client-js' +} + +plugin react { + provider = '@zenstackhq/react' + output = 'lib/hooks' +} + +model User { + id String @id + name String + posts Post[] + + @@allow('create,read', true) + @@allow('update,delete', auth() == this) +} + +model Post { + id String @id + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String? + published Boolean @default(false) + + @@allow('all', auth() == this) + @@allow('read', published) +} diff --git a/tests/integration/tests/nextjs/test-project/server/db.ts b/tests/integration/tests/nextjs/test-project/server/db.ts new file mode 100644 index 000000000..c05a1eb79 --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/server/db.ts @@ -0,0 +1,12 @@ +import { PrismaClient } from '@prisma/client'; + +declare global { + // eslint-disable-next-line no-var + var prisma: PrismaClient | undefined; +} + +export const prisma = global.prisma || new PrismaClient(); + +if (process.env.NODE_ENV !== 'production') { + global.prisma = prisma; +} diff --git a/tests/integration/tests/nextjs/test-project/sqlite.zmodel b/tests/integration/tests/nextjs/test-project/sqlite.zmodel new file mode 100644 index 000000000..1648827a1 --- /dev/null +++ b/tests/integration/tests/nextjs/test-project/sqlite.zmodel @@ -0,0 +1,33 @@ +datasource db { + provider = 'sqlite' + url = 'file:./dev.db' +} + +generator js { + provider = 'prisma-client-js' +} + +plugin react { + provider = '@zenstackhq/react' + output = 'lib/hooks' +} + +model User { + id String @id + name String + posts Post[] + + @@allow('create,read', true) + @@allow('update,delete', auth() == this) +} + +model Post { + id String @id + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String? + published Boolean @default(false) + + @@allow('all', auth() == this) + @@allow('read', published) +} diff --git a/doc-serve/tsconfig.json b/tests/integration/tests/nextjs/test-project/tsconfig.json similarity index 100% rename from doc-serve/tsconfig.json rename to tests/integration/tests/nextjs/test-project/tsconfig.json diff --git a/tests/integration/tests/omit.test.ts b/tests/integration/tests/omit.test.ts deleted file mode 100644 index 21d9a4798..000000000 --- a/tests/integration/tests/omit.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; - -describe('Omit attribute tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/omit.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - it('omit test', async () => { - await makeClient('/api/data/User') - .post('/') - .send({ - include: { profile: true }, - data: { - id: '1', - password: 'abc123', - profile: { - create: { - image: 'an image', - }, - }, - }, - }) - .expect(201) - .expect((resp) => { - expect(resp.body.password).toBeUndefined(); - expect(resp.body.profile.image).toBeUndefined(); - }); - - await makeClient('/api/data/User/1', undefined, { - include: { profile: true }, - }) - .get('/') - .expect((resp) => { - expect(resp.body.password).toBeUndefined(); - expect(resp.body.profile.image).toBeUndefined(); - }); - }); -}); diff --git a/tests/integration/tests/omit.zmodel b/tests/integration/tests/omit.zmodel deleted file mode 100644 index 01677619e..000000000 --- a/tests/integration/tests/omit.zmodel +++ /dev/null @@ -1,21 +0,0 @@ -datasource db { - provider = 'sqlite' - url = 'file:./omit.db' -} - -model User { - id String @id @default(cuid()) - password String @omit - profile Profile? - - @@allow('all', true) -} - -model Profile { - id String @id @default(cuid()) - user User @relation(fields: [userId], references: [id]) - userId String @unique - image String @omit - - @@allow('all', true) -} \ No newline at end of file diff --git a/tests/integration/tests/operation-coverate.test.ts b/tests/integration/tests/operation-coverate.test.ts deleted file mode 100644 index 9cec10ec2..000000000 --- a/tests/integration/tests/operation-coverate.test.ts +++ /dev/null @@ -1,1123 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import { ServerErrorCode } from '../../../packages/runtime/src/types'; - -describe('Operation Coverage Tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/operations.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - //#region Empty Policy - - it('empty policy', async () => { - const client = makeClient('/api/data/EmptyPolicy'); - await client.post('/').send({ data: {} }).expect(403); - await client - .get('/') - .expect((resp) => expect(resp.body).toHaveLength(0)); - }); - - it('nested write empty policy to-many', async () => { - const client = makeClient('/api/data/M1'); - await client - .post('/') - .send({ - data: { - m2: { - create: [{}], - }, - }, - }) - .expect(403); - }); - - it('nested write empty policy to-one', async () => { - const client = makeClient('/api/data/M1'); - await client - .post('/') - .send({ - data: { - m3: { - create: {}, - }, - }, - }) - .expect(403); - }); - - //#endregion - - //#region Toplevel operations - - it('toplevel find and get', async () => { - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - id: '1', - value: 1, - }, - }) - .expect(403) - .expect((resp) => - expect(resp.body.code).toBe( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ) - ); - - await makeClient('/api/data/M4') - .get('/') - .expect((resp) => expect(resp.body).toHaveLength(0)); - - await makeClient('/api/data/M4/1').get('/').expect(404); - - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - id: '2', - value: 2, - }, - }) - .expect(201); - - await makeClient('/api/data/M4') - .get('/') - .expect((resp) => expect(resp.body).toHaveLength(1)); - - await makeClient('/api/data/M4/2').get('/').expect(200); - }); - - it('toplevel create, update and delete', async () => { - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - value: 0, - }, - }) - .expect(403); - - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - id: '1', - value: 1, - }, - }) - .expect(403) - .expect((resp) => - expect(resp.body.code).toBe( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ) - ); - - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - value: 2, - }, - }) - .expect(201); - - await makeClient('/api/data/M4/1') - .put('/') - .send({ - data: { - value: 1, - }, - }) - .expect(403); - - await makeClient('/api/data/M4') - .post('/') - .send({ - data: { - id: '2', - value: 2, - }, - }) - .expect(201); - - await makeClient('/api/data/M4/2') - .put('/') - .send({ - data: { - value: 3, - }, - }) - .expect(200); - - await makeClient('/api/data/M4/1').delete('/').expect(403); - await makeClient('/api/data/M4/2').delete('/').expect(200); - }); - - //#endregion - - //#region Nested To-one - - it('nested to-one writes', async () => { - // reject because nested m5 fails 'create' rule - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - m6: { - create: { - value: 0, - }, - }, - }, - }) - .expect(403); - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '1', - m6: { - create: { - id: '1', - value: 1, - }, - }, - }, - }) - .expect(201); - // reject because nested m6 fails 'update' rule - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m6: { - update: { - value: 2, - }, - }, - }, - }) - .expect(403); - // nest create during update - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '1.1', - }, - }) - .expect(201); - // reject because nested m6 fail 'create' rule - await makeClient('/api/data/M5/1.1') - .put('/') - .send({ - data: { - m6: { - create: { - value: 0, - }, - }, - }, - }) - .expect(403); - // should succeed now when the nested create meets policy - await makeClient('/api/data/M5/1.1') - .put('/') - .send({ - data: { - m6: { - create: { - value: 1, - }, - }, - }, - }) - .expect(200); - - // test nested delete - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '2', - m6: { - create: { - id: '2', - value: 2, - }, - }, - }, - }) - .expect(201); - - // reject because m6 fails 'delete' rule - await makeClient('/api/data/M5/2') - .put('/') - .send({ - data: { - id: '2', - m6: { - delete: true, - }, - }, - }) - .expect(403); - - await makeClient('/api/data/M5/2') - .put('/') - .send({ - data: { - m6: { - update: { - value: 3, - }, - }, - }, - }) - .expect(200); - - // item should be still there - await makeClient('/api/data/M6/2').get('/').expect(200); - - await makeClient('/api/data/M5/2') - .put('/') - .send({ - data: { - id: '2', - m6: { - delete: true, - }, - }, - }) - .expect(200); - await makeClient('/api/data/M6/2').get('/').expect(404); - }); - - //#endregion - - //#region Nested To-many - - it('nested to-many create denied', async () => { - const client = makeClient('/api/data/M5'); - - // single-create - await client - .post('/') - .send({ - data: { - m7: { - create: { - value: 0, - }, - }, - }, - }) - .expect(403); - - // multi-create - await client - .post('/') - .send({ - data: { - m7: { - create: [ - { - value: 0, - }, - { - value: 1, - }, - ], - }, - }, - }) - .expect(403); - }); - - it('nested to-many create allowed', async () => { - const client = makeClient('/api/data/M5'); - - // single-create - await client - .post('/') - .send({ - data: { - m7: { - create: { - value: 1, - }, - }, - }, - }) - .expect(201); - - // multi-create - await client - .post('/') - .send({ - data: { - m7: { - create: [ - { - value: 1, - }, - { - value: 2, - }, - ], - }, - }, - }) - .expect(201); - }); - - it('nested to-many update', async () => { - // nested entities don't satisify policy before update, so should be excluded - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '1', - m7: { - create: [ - { - id: '1', - value: 1, - }, - ], - }, - }, - }) - .expect(201); - - // m7 fails 'update' rule - await makeClient('/api/data/M5/1') - .put('/') - .send({ - include: { m7: true }, - data: { - m7: { - update: { - where: { id: '1' }, - data: { value: 2 }, - }, - }, - }, - }) - .expect(403); - - // nested entities satisify policy before update, so should be included for update - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '2', - m7: { - create: { - id: '2', - value: 2, - }, - }, - }, - }) - .expect(201); - - await makeClient('/api/data/M5/2') - .put('/') - .send({ - include: { m7: true }, - data: { - m7: { - update: { - where: { id: '2' }, - data: { value: 3 }, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m7).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: '2', value: 3 }), - ]) - ); - }); - }); - - it('nested to-many update with create', async () => { - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '1', - m7: { - create: { - value: 1, - }, - }, - }, - }) - .expect(201); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - create: { - id: '2', - value: 0, - }, - }, - }, - }) - .expect(403); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - create: [ - { - value: 0, - }, - { - value: 1, - }, - ], - }, - }, - }) - .expect(403); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - include: { m7: true }, - data: { - m7: { - create: [ - { - value: 1, - }, - { - value: 2, - }, - ], - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m7).toHaveLength(3); - }); - }); - - it('nested to-many update with delete', async () => { - await makeClient('/api/data/M5') - .post('/') - .send({ - data: { - id: '1', - m7: { - create: [ - { - id: '1', - value: 1, - }, - { - id: '2', - value: 2, - }, - { - id: '3', - value: 3, - }, - { - id: '4', - value: 4, - }, - { - id: '5', - value: 5, - }, - ], - }, - }, - }) - .expect(201); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - delete: { id: '1' }, - }, - }, - }) - .expect(403); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - deleteMany: { OR: [{ id: '2' }, { id: '3' }] }, - }, - }, - }) - .expect(403); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - delete: { id: '3' }, - }, - }, - }) - .expect(200); - await makeClient('/api/data/M7/3').get('/').expect(404); - - await makeClient('/api/data/M5/1') - .put('/') - .send({ - data: { - m7: { - deleteMany: { value: { gte: 4 } }, - }, - }, - }) - .expect(200); - await makeClient('/api/data/M7/4').get('/').expect(404); - await makeClient('/api/data/M7/5').get('/').expect(404); - }); - - //#endregion - - //#region Nested writes with nested read - - it('create with nested read', async () => { - // TODO - }); - - it('update with nested read', async () => { - await makeClient('/api/data/M8') - .post('/') - .send({ - data: { - id: '1', - m9: { - create: { - value: 0, - }, - }, - m10: { - create: [ - { - id: '1', - value: 0, - }, - { - id: '2', - value: 0, - }, - ], - }, - }, - }) - .expect(201); - - // reject because m9 fails 'read' rule - await makeClient('/api/data/M8/1') - .put('/') - .send({ - include: { m9: true }, - data: { - m9: { - update: { - value: 1, - }, - }, - }, - }) - .expect(403) - .expect((resp) => - expect(resp.body.code).toBe( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ) - ); - - // m9 can be returned now, m10 should be filtered out because of 'read' rule - await makeClient('/api/data/M8/1') - .put('/') - .send({ - include: { m9: true, m10: true }, - data: { - m9: { - update: { - value: 2, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m9.value).toBe(2); - expect(resp.body.m10).toHaveLength(0); - }); - - // one of m10 entities pass 'read' rule now - await makeClient('/api/data/M8/1') - .put('/') - .send({ - select: { m10: true }, - data: { - m10: { - update: { - where: { id: '1' }, - data: { value: 2 }, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m10).toHaveLength(1); - }); - }); - - //#endregion - - //#region Deep nesting - - it('deep nested create', async () => { - // deep create success - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - id: '1', - m12: { - create: { - id: 'm12-1', - value: 1, - m13: { - create: { - id: 'm13-1', - value: 11, - }, - }, - m14: { - create: [ - { id: 'm14-1', value: 21 }, - { id: 'm14-2', value: 22 }, - ], - }, - }, - }, - }, - }) - .expect(201); - - // deep connect success - await makeClient('/api/data/M11') - .post('/') - .send({ - include: { m12: { include: { m13: true, m14: true } } }, - data: { - id: '2', - m12: { - create: { - value: 2, - m13: { - connect: { - id: 'm13-1', - }, - }, - m14: { - connect: [{ id: 'm14-1' }], - connectOrCreate: [ - { - where: { id: 'm14-2' }, - create: { id: 'm14-new', value: 22 }, - }, - { - where: { id: 'm14-3' }, - create: { id: 'm14-3', value: 23 }, - }, - ], - }, - }, - }, - }, - }) - .expect(201) - .expect((resp) => { - expect(resp.body.m12.m13.id).toBe('m13-1'); - expect(resp.body.m12.m14[0].id).toBe('m14-1'); - expect(resp.body.m12.m14[1].id).toBe('m14-2'); - expect(resp.body.m12.m14[2].id).toBe('m14-3'); - }); - - // deep create violation - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - m12: { - create: { - value: 1, - m14: { - create: [{ value: 20 }, { value: 22 }], - }, - }, - }, - }, - }) - .expect(403); - - // deep create violation via deep policy: @@deny('create', m12.m14?[value == 100]) - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - m12: { - create: { - value: 1, - m14: { - create: { value: 100 }, - }, - }, - }, - }, - }) - .expect(403); - - // deep connect violation via deep policy: @@deny('create', m12.m14?[value == 100]) - await makeClient('/api/data/M14') - .post('/') - .send({ - data: { - id: 'm14-value-100', - value: 100, - }, - }) - .expect(201); - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - m12: { - create: { - value: 1, - m14: { - connect: { id: 'm14-value-100' }, - }, - }, - }, - }, - }) - .expect(403); - - // create read-back filter: M14 @@deny('read', value == 200) - await makeClient('/api/data/M11') - .post('/') - .send({ - include: { m12: { include: { m14: true } } }, - data: { - m12: { - create: { - value: 1, - m14: { - create: [{ value: 200 }, { value: 201 }], - }, - }, - }, - }, - }) - .expect(201) - .expect((resp) => { - expect(resp.body.m12.m14).toHaveLength(1); - }); - - // create read-back rejection: M13 @@deny('read', value == 200) - await makeClient('/api/data/M11') - .post('/') - .send({ - include: { m12: { include: { m13: true } } }, - data: { - m12: { - create: { - value: 1, - m13: { - create: { value: 200 }, - }, - }, - }, - }, - }) - .expect(403); - }); - - it('deep nested update', async () => { - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - id: '1', - }, - }) - .expect(201); - - // deep update with create success - await makeClient('/api/data/M11/1') - .put('/') - .send({ - include: { m12: { include: { m13: true, m14: true } } }, - data: { - m12: { - create: { - id: 'm12-1', - value: 2, - m13: { - create: { - id: 'm13-1', - value: 11, - }, - }, - m14: { - create: [ - { id: 'm14-1', value: 22 }, - { id: 'm14-2', value: 23 }, - ], - }, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m12.m13.id).toBe('m13-1'); - expect(resp.body.m12.m14).toHaveLength(2); - }); - - // deep update with connect/disconnect/delete success - await makeClient('/api/data/M14') - .post('/') - .send({ - data: { - id: 'm14-3', - value: 23, - }, - }) - .expect(201); - await makeClient('/api/data/M11/1') - .put('/') - .send({ - include: { m12: { include: { m14: true } } }, - data: { - m12: { - update: { - m14: { - connect: [{ id: 'm14-3' }], - disconnect: { id: 'm14-1' }, - delete: { id: 'm14-2' }, - }, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m12.m14).toHaveLength(1); - expect(resp.body.m12.m14[0].id).toBe('m14-3'); - }); - - // reconnect m14-1, create m14-2 - await makeClient('/api/data/M11/1') - .put('/') - .send({ - include: { m12: { include: { m14: true } } }, - data: { - m12: { - update: { - m14: { - connect: [{ id: 'm14-1' }], - create: { id: 'm14-2', value: 23 }, - }, - }, - }, - }, - }) - .expect(200); - - // deep update violation - await makeClient('/api/data/M11/1') - .put('/') - .send({ - data: { - m12: { - update: { - m14: { - create: { value: 20 }, - }, - }, - }, - }, - }) - .expect(403); - - // deep update violation via deep policy: @@deny('update', m12.m14?[value == 101]) - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - id: '2', - m12: { - create: { - value: 2, - m14: { - create: { - id: 'm14-101', - value: 101, - }, - }, - }, - }, - }, - }) - .expect(201); - await makeClient('/api/data/M11/2') - .put('/') - .send({ - data: { - m12: { - update: { - m14: { - updateMany: { - where: { value: { gt: 0 } }, - data: { value: 102 }, - }, - }, - }, - }, - }, - }) - .expect(403); - - // update read-back filter: M14 @@deny('read', value == 200) - await makeClient('/api/data/M11/1') - .put('/') - .send({ - include: { m12: { include: { m14: true } } }, - data: { - m12: { - update: { - m14: { - update: { - where: { id: 'm14-1' }, - data: { value: 200 }, - }, - }, - }, - }, - }, - }) - .expect(200) - .expect((resp) => { - expect(resp.body.m12.m14).toHaveLength(2); - expect(resp.body.m12.m14.map((d: any) => d.id)).not.toContain( - 'm14-1' - ); - }); - - // update read-back rejection: M13 @@deny('read', value == 200) - await makeClient('/api/data/M11/1') - .put('/') - .send({ - include: { m12: { include: { m13: true } } }, - data: { - m12: { - update: { - m13: { - update: { value: 200 }, - }, - }, - }, - }, - }) - .expect(403); - }); - - it('deep nested delete', async () => { - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - id: '1', - m12: { - create: { - value: 1, - m14: { - create: [{ value: 200 }, { value: 22 }], - }, - }, - }, - }, - }) - .expect(201); - - // delete read-back filter: M14 @@deny('read', value == 200) - await makeClient(`/api/data/M11/1`, undefined, { - include: { m12: { select: { m14: true } } }, - }) - .delete('/') - .expect(200) - .expect((resp) => { - expect(resp.body.m12.m14).toHaveLength(1); - }); - - await makeClient('/api/data/M11') - .post('/') - .send({ - data: { - id: '2', - m12: { - create: { - value: 1, - m13: { - create: { value: 200 }, - }, - }, - }, - }, - }) - .expect(201); - - // delete read-back reject: M13 @@deny('read', value == 200) - await makeClient(`/api/data/M11/1`, undefined, { - include: { m12: { select: { m13: { id: true } } } }, - }) - .delete('/') - .expect(403); - }); - - //#endregion -}); diff --git a/tests/integration/tests/operations.zmodel b/tests/integration/tests/operations.zmodel deleted file mode 100644 index aa3f653f9..000000000 --- a/tests/integration/tests/operations.zmodel +++ /dev/null @@ -1,150 +0,0 @@ -datasource db { - provider = 'sqlite' - url = 'file:./operations.db' -} - -model EmptyPolicy { - id String @id @default(uuid()) -} - -model M1 { - id String @id @default(uuid()) - m2 M2[] - m3 M3? - - @@allow('all', true) -} - -model M2 { - id String @id @default(uuid()) - m1 M1 @relation(fields: [m1Id], references:[id]) - m1Id String -} - -model M3 { - id String @id @default(uuid()) - m1 M1 @relation(fields: [m1Id], references:[id]) - m1Id String @unique -} - -model M4 { - id String @id @default(uuid()) - value Int - - @@allow('read', value > 1) - @@allow('create', value > 0) - @@allow('update', value > 1) - @@allow('delete', value > 2) -} - -model M5 { - id String @id @default(uuid()) - m6 M6? - m7 M7[] - - @@allow('all', true) -} - -model M6 { - id String @id @default(uuid()) - value Int - m5 M5 @relation(fields: [m5Id], references:[id]) - m5Id String @unique - - @@allow('read', true) - @@allow('create', value > 0) - @@allow('update', value > 1) - @@allow('delete', value > 2) -} - -model M7 { - id String @id @default(uuid()) - value Int - m5 M5 @relation(fields: [m5Id], references:[id]) - m5Id String - - @@allow('read', true) - @@allow('create', value > 0) - @@allow('update', value > 1) - @@allow('delete', value > 2) -} - -model M8 { - id String @id @default(uuid()) - m9 M9? - m10 M10[] - - @@allow('all', true) -} - -model M9 { - id String @id @default(uuid()) - value Int - m8 M8 @relation(fields: [m8Id], references:[id]) - m8Id String @unique - - @@allow('read', value > 1) - @@allow('create,update', true) -} - -model M10 { - id String @id @default(uuid()) - value Int - m8 M8 @relation(fields: [m8Id], references:[id]) - m8Id String - - @@allow('read', value > 1) - @@allow('create,update', true) -} - -// M11 - M12 - M13 -// -* M14 -model M11 { - id String @id @default(cuid()) - m12 M12? - - @@allow('all', true) - @@deny('create', m12.m14?[value == 100]) - @@deny('update', m12.m14?[value == 101]) -} - -model M12 { - id String @id @default(cuid()) - value Int - m11 M11 @relation(fields: [m11Id], references: [id], onDelete: Cascade) - m11Id String @unique - - m13 M13? - m14 M14[] - - @@allow('read', true) - @@allow('create', value > 0) - @@allow('update', value > 1) - @@allow('delete', value > 2) -} - -model M13 { - id String @id @default(cuid()) - value Int - m12 M12 @relation(fields: [m12Id], references: [id], onDelete: Cascade) - m12Id String @unique - - @@allow('read', true) - @@allow('create', value > 10) - @@allow('update', value > 11) - @@allow('delete', value > 12) - @@deny('read', value == 200) -} - -model M14 { - id String @id @default(cuid()) - value Int - m12 M12? @relation(fields: [m12Id], references: [id], onDelete: Cascade) - m12Id String? - - @@allow('read', true) - @@allow('create', value > 20) - @@allow('update', value > 21) - @@allow('delete', value > 22) - @@deny('read', value == 200) -} diff --git a/tests/integration/tests/password.test.ts b/tests/integration/tests/password.test.ts deleted file mode 100644 index 2bba24d78..000000000 --- a/tests/integration/tests/password.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import { compareSync } from 'bcryptjs'; - -describe('Password attribute tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/password.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - it('password direct write test', async () => { - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123', - }, - }) - .expect(async (resp) => - expect(compareSync('abc123', resp.body.password)).toBeTruthy() - ); - - await makeClient('/api/data/User/1') - .put('/') - .send({ - data: { - password: 'abc456', - }, - }) - .expect(async (resp) => - expect(compareSync('abc456', resp.body.password)).toBeTruthy() - ); - }); - - it('password indirect write test', async () => { - await makeClient('/api/data/User') - .post('/') - .send({ - data: { - id: '1', - password: 'abc123', - profile: { - create: { - id: '1', - }, - }, - }, - }); - - await makeClient('/api/data/Profile/1') - .put('/') - .send({ - include: { user: true }, - data: { - user: { - update: { - password: 'abc456', - }, - }, - }, - }) - .expect(async (resp) => - expect( - compareSync('abc456', resp.body.user.password) - ).toBeTruthy() - ); - }); -}); diff --git a/tests/integration/tests/password.zmodel b/tests/integration/tests/password.zmodel deleted file mode 100644 index 7a913d1d4..000000000 --- a/tests/integration/tests/password.zmodel +++ /dev/null @@ -1,20 +0,0 @@ -datasource db { - provider = 'sqlite' - url = 'file:./password.db' -} - -model User { - id String @id @default(cuid()) - password String @password(saltLength: 16) - profile Profile? - - @@allow('all', true) -} - -model Profile { - id String @id @default(cuid()) - user User @relation(fields: [userId], references: [id]) - userId String @unique - - @@allow('all', true) -} \ No newline at end of file diff --git a/tests/integration/tests/todo.zmodel b/tests/integration/tests/schema/todo.zmodel similarity index 82% rename from tests/integration/tests/todo.zmodel rename to tests/integration/tests/schema/todo.zmodel index 1529c21fc..001e78dd0 100644 --- a/tests/integration/tests/todo.zmodel +++ b/tests/integration/tests/schema/todo.zmodel @@ -7,6 +7,21 @@ datasource db { url = 'file:./todo.db' } +generator js { + provider = 'prisma-client-js' + output = '../.prisma' +} + +plugin meta { + provider = '@zenstack/model-meta' + output = '.zenstack' +} + +plugin policy { + provider = '@zenstack/access-policy' + output = '.zenstack' +} + /* * Model for a space in which users can collaborate on Lists and Todos @@ -69,6 +84,7 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt email String @unique @email + password String? @password @omit emailVerified DateTime? name String? ownedSpaces Space[] @@ -108,8 +124,12 @@ model List { // can be read by owner or space members (only if not private) @@allow('read', owner == auth() || (space.members?[user == auth()] && !private)) - // when create/udpate, owner must be set to current user, and user must be in the space - @@allow('create,update', owner == auth() && space.members?[user == auth()]) + // when create, owner must be set to current user, and user must be in the space + @@allow('create', owner == auth() && space.members?[user == auth()]) + + // when create, owner must be set to current user, and user must be in the space + // update is not allowed to change owner + @@allow('update', owner == auth()&& space.members?[user == auth()] && future().owner == owner) // can be deleted by owner @@allow('delete', owner == auth()) @@ -135,4 +155,7 @@ model Todo { // owner has full access, also space members have full access (if the parent List is not private) @@allow('all', list.owner == auth()) @@allow('all', list.space.members?[user == auth()] && !list.private) + + // update is not allowed to change owner + @@deny('update', future().owner != owner) } diff --git a/tests/integration/tests/todo-e2e.test.ts b/tests/integration/tests/todo-e2e.test.ts deleted file mode 100644 index 72455b374..000000000 --- a/tests/integration/tests/todo-e2e.test.ts +++ /dev/null @@ -1,592 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import { ServerErrorCode } from '../../../packages/runtime/src/types'; - -describe('Todo E2E Tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/todo.zmodel'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - it('user', async () => { - const userClient = makeClient('/api/data/User'); - const user1 = { - id: 'user1', - email: 'user1@zenstack.dev', - name: 'User 1', - }; - const user2 = { - id: 'user2', - email: 'user2@zenstack.dev', - name: 'User 2', - }; - const user1Client = makeClient('/api/data/User/user1', user1.id); - const user2Client = makeClient('/api/data/User/user2', user1.id); - - // create user1 - await userClient - .post('/') - .send({ - data: user1, - }) - .expect(403) - .expect((resp) => { - expect(resp.body.code).toBe( - ServerErrorCode.READ_BACK_AFTER_WRITE_DENIED - ); - }); - - const credClient = makeClient('/api/data/User', user1.id); - - // find - await credClient - .get('/') - .expect(200) - .expect((resp) => { - expect(resp.body).toEqual([expect.objectContaining(user1)]); - }); - - // get - await user1Client - .get('/') - .expect(200) - .expect((resp) => - expect(resp.body).toEqual(expect.objectContaining(user1)) - ); - await user2Client.get('/').expect(404); - - // create user2 - await userClient.post('/').send({ - data: user2, - }); - - // find with user1 should only get user1 - await credClient - .get('/') - .expect(200) - .expect((resp) => { - expect(resp.body).toHaveLength(1); - expect(resp.body).toEqual([expect.objectContaining(user1)]); - }); - - // get user2 as user1 - await user2Client.get('/').expect(404); - - // add both users into the same space - const spaceClient = makeClient('/api/data/Space', user1.id); - await spaceClient - .post('/') - .send({ - data: { - name: 'Space 1', - slug: 'space1', - owner: { connect: { id: user1.id } }, - members: { - create: [ - { - user: { connect: { id: user1.id } }, - role: 'ADMIN', - }, - { - user: { connect: { id: user2.id } }, - role: 'USER', - }, - ], - }, - }, - }) - .expect(201); - - // now both user1 and user2 should be visible - await credClient - .get('/') - .expect((resp) => expect(resp.body).toHaveLength(2)); - await user2Client - .get('/') - .expect(200) - .expect((resp) => - expect(resp.body).toEqual(expect.objectContaining(user2)) - ); - - // update user2 as user1 - await user2Client - .put('/') - .send({ - data: { - name: 'hello', - }, - }) - .expect(403); - - // update user1 as user1 - await user1Client - .put('/') - .send({ - data: { - name: 'hello', - }, - }) - .expect(200) - .expect((resp) => expect(resp.body.name).toBe('hello')); - - // delete user2 as user1 - await user2Client.delete('/').expect(403); - - // delete user1 as user1 - await user1Client.delete('/').expect(200); - await user1Client.get('/').expect(404); - }); - - it('todo list', async () => { - await createSpaceAndUsers(); - - const listClient = makeClient('/api/data/List'); - await listClient - .post('/') - .send({ - data: { - id: 'list1', - title: 'List 1', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(403); - - const credClient = makeClient('/api/data/List', user1.id); - await credClient - .post('/') - .send({ - data: { - id: 'list1', - title: 'List 1', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(201); - - await credClient - .get('/') - .expect(200) - .expect((resp) => expect(resp.body).toHaveLength(1)); - - await listClient - .get('/') - .expect(200) - .expect((resp) => expect(resp.body).toHaveLength(0)); - - const list1Client = makeClient('/api/data/List/list1'); - await list1Client.get('/').expect(404); - - // accessible to owner - const list1CredClientUser1 = makeClient( - '/api/data/List/list1', - user1.id - ); - await list1CredClientUser1 - .get('/') - .expect(200) - .expect((resp) => - expect(resp.body).toEqual( - expect.objectContaining({ id: 'list1', title: 'List 1' }) - ) - ); - - // accessible to user in the space - const list1CredClientUser2 = makeClient( - '/api/data/List/list1', - user2.id - ); - await list1CredClientUser2.get('/').expect(200); - - // inaccessible to user not in the space - const list1CredClientUser3 = makeClient( - '/api/data/List/list1', - user3.id - ); - await list1CredClientUser3.get('/').expect(404); - - // make a private list - await credClient - .post('/') - .send({ - data: { - id: 'list2', - title: 'List 2', - private: true, - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(201); - - // accessible to owner - const list2CredClientUser1 = makeClient( - '/api/data/List/list2', - user1.id - ); - await list2CredClientUser1.get('/').expect(200); - - // inaccessible to other user in the space - const list2CredClientUser2 = makeClient( - '/api/data/List/list2', - user2.id - ); - await list2CredClientUser2.get('/').expect(404); - - // create a list which doesn't match credential should fail - await credClient - .post('/') - .send({ - data: { - id: 'list3', - title: 'List 3', - owner: { connect: { id: user2.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(403); - - // create a list which doesn't match credential's space should fail - await credClient - .post('/') - .send({ - data: { - id: 'list3', - title: 'List 3', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space2.id } }, - }, - }) - .expect(403); - - // update list - await list1CredClientUser1 - .put('/') - .send({ - data: { - title: 'List 1 updated', - }, - }) - .expect(200) - .expect((resp) => expect(resp.body.title).toBe('List 1 updated')); - - await list1CredClientUser2 - .put('/') - .send({ - data: { - title: 'List 1 updated', - }, - }) - .expect(403); - - // delete list - await list1CredClientUser2.delete('/').expect(403); - await list1CredClientUser1.delete('/').expect(200); - await list1CredClientUser1.get('/').expect(404); - }); - - it('todo list with empty user id', async () => { - await createSpaceAndUsers(); - - await makeClient('/api/data/List', user1.id) - .post('/') - .send({ - data: { - id: 'list1', - title: 'List 1', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(201); - - await makeClient('/api/data/List', '') - .get('/') - .expect((resp) => expect(resp.body).toHaveLength(0)); - }); - - it('todo', async () => { - await createSpaceAndUsers(); - - const listClient = makeClient('/api/data/List', user1.id); - await listClient - .post('/') - .send({ - data: { - id: 'list1', - title: 'List 1', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(201); - - const todoClientUser1 = makeClient('/api/data/Todo', user1.id); - const todoClientUser2 = makeClient('/api/data/Todo', user2.id); - - // create - await todoClientUser1 - .post('/') - .send({ - data: { - id: 'todo1', - title: 'Todo 1', - owner: { connect: { id: user1.id } }, - list: { - connect: { id: 'list1' }, - }, - }, - }) - .expect(201); - await todoClientUser2 - .post('/') - .send({ - data: { - id: 'todo2', - title: 'Todo 2', - owner: { connect: { id: user2.id } }, - list: { - connect: { id: 'list1' }, - }, - }, - }) - .expect(201); - - // read - await todoClientUser1 - .get('/') - .expect(200) - .expect((resp) => expect(resp.body).toHaveLength(2)); - await todoClientUser2 - .get('/') - .expect(200) - .expect((resp) => expect(resp.body).toHaveLength(2)); - - const todo1ClientUser1 = makeClient('/api/data/Todo/todo1', user1.id); - const todo2ClientUser1 = makeClient('/api/data/Todo/todo2', user1.id); - - // update, user in the same space can freely update - await todo1ClientUser1 - .put('/') - .send({ - data: { - title: 'Todo 1 updated', - }, - }) - .expect(200); - await todo2ClientUser1 - .put('/') - .send({ - data: { - title: 'Todo 2 updated', - }, - }) - .expect(200); - - // create a private list - await listClient - .post('/') - .send({ - data: { - id: 'list2', - private: true, - title: 'List 2', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }) - .expect(201); - - // create - await todoClientUser1 - .post('/') - .send({ - data: { - id: 'todo3', - title: 'Todo 3', - owner: { connect: { id: user1.id } }, - list: { - connect: { id: 'list2' }, - }, - }, - }) - .expect(201); - - // reject because list2 is private - await todoClientUser2 - .post('/') - .send({ - data: { - id: 'todo4', - title: 'Todo 4', - owner: { connect: { id: user2.id } }, - list: { - connect: { id: 'list2' }, - }, - }, - }) - .expect(403); - - // update, only owner can update todo in a private list - const todo3ClientUser1 = makeClient('/api/data/Todo/todo3', user1.id); - const todo3ClientUser2 = makeClient('/api/data/Todo/todo3', user2.id); - await todo3ClientUser1 - .put('/') - .send({ - data: { - title: 'Todo 3 updated', - }, - }) - .expect(200); - await todo3ClientUser2 - .put('/') - .send({ - data: { - title: 'Todo 3 updated', - }, - }) - .expect(403); - }); - - it('relation query', async () => { - await createSpaceAndUsers(); - const listClient = makeClient('/api/data/List', user1.id); - await listClient.post('/').send({ - data: { - id: 'list1', - title: 'List 1', - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }); - await listClient.post('/').send({ - data: { - id: 'list2', - title: 'List 2', - private: true, - owner: { connect: { id: user1.id } }, - space: { connect: { id: space1.id } }, - }, - }); - - const spaceClientUser1 = makeClient( - `/api/data/Space/space1`, - user1.id, - { - include: { lists: true }, - } - ); - await spaceClientUser1.get('/').expect((resp) => { - expect(resp.body.lists).toHaveLength(2); - }); - - const spaceClientUser2 = makeClient( - `/api/data/Space/space1`, - user2.id, - { - include: { lists: true }, - } - ); - await spaceClientUser2.get('/').expect((resp) => { - expect(resp.body.lists).toHaveLength(1); - }); - }); -}); - -const user1 = { - id: 'user1', - email: 'user1@zenstack.dev', - name: 'User 1', -}; - -const user2 = { - id: 'user2', - email: 'user2@zenstack.dev', - name: 'User 2', -}; - -const user3 = { - id: 'user3', - email: 'user3@zenstack.dev', - name: 'User 3', -}; - -const space1 = { - id: 'space1', - name: 'Space 1', - slug: 'space1', -}; - -const space2 = { - id: 'space2', - name: 'Space 2', - slug: 'space2', -}; - -async function createSpaceAndUsers() { - const userClient = makeClient('/api/data/User'); - // create users - await userClient.post('/').send({ - data: user1, - }); - await userClient.post('/').send({ - data: user2, - }); - await userClient.post('/').send({ - data: user3, - }); - - // add user1 and user2 into space1 - const spaceClientUser1 = makeClient('/api/data/Space', user1.id); - await spaceClientUser1 - .post('/') - .send({ - data: { - ...space1, - members: { - create: [ - { - user: { connect: { id: user1.id } }, - role: 'ADMIN', - }, - { - user: { connect: { id: user2.id } }, - role: 'USER', - }, - ], - }, - }, - }) - .expect(201); - - // add user3 to space2 - const spaceClientUser3 = makeClient('/api/data/Space', user3.id); - await spaceClientUser3 - .post('/') - .send({ - data: { - ...space2, - members: { - create: [ - { - user: { connect: { id: user3.id } }, - role: 'ADMIN', - }, - ], - }, - }, - }) - .expect(201); -} diff --git a/tests/integration/tests/trpc/generation.test.ts b/tests/integration/tests/trpc/generation.test.ts new file mode 100644 index 000000000..e6a12dda8 --- /dev/null +++ b/tests/integration/tests/trpc/generation.test.ts @@ -0,0 +1,32 @@ +import fs from 'fs'; +import fse from 'fs-extra'; +import path from 'path'; +import { run } from '../../utils'; + +describe('tRPC Routers Generation Tests', () => { + let origDir: string; + + beforeAll(() => { + origDir = process.cwd(); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('basic', async () => { + const testDir = path.join(__dirname, './test-run/basic'); + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + fs.mkdirSync(testDir, { recursive: true }); + fse.copySync(path.join(__dirname, './test-project'), testDir); + + const nodePath = path.resolve(path.join(__dirname, '../../node_modules')); + + process.chdir(testDir); + run('npm install'); + run('npx zenstack generate --schema ./todo.zmodel', { NODE_PATH: nodePath }); + run('npm run build', { NODE_PATH: nodePath }); + }); +}); diff --git a/samples/todo/.gitignore b/tests/integration/tests/trpc/test-project/.gitignore similarity index 91% rename from samples/todo/.gitignore rename to tests/integration/tests/trpc/test-project/.gitignore index 6fb849099..c87c9b392 100644 --- a/samples/todo/.gitignore +++ b/tests/integration/tests/trpc/test-project/.gitignore @@ -34,6 +34,3 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts - -/.zenstack -/zenstack/schema.prisma diff --git a/tests/integration/tests/trpc/test-project/.npmrc b/tests/integration/tests/trpc/test-project/.npmrc new file mode 100644 index 000000000..e38856e8a --- /dev/null +++ b/tests/integration/tests/trpc/test-project/.npmrc @@ -0,0 +1 @@ +cache=../../../.npmcache diff --git a/tests/integration/tests/trpc/test-project/README.md b/tests/integration/tests/trpc/test-project/README.md new file mode 100644 index 000000000..c87e0421d --- /dev/null +++ b/tests/integration/tests/trpc/test-project/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/tests/integration/tests/trpc/test-project/lib/trpc.ts b/tests/integration/tests/trpc/test-project/lib/trpc.ts new file mode 100644 index 000000000..677b21cac --- /dev/null +++ b/tests/integration/tests/trpc/test-project/lib/trpc.ts @@ -0,0 +1,32 @@ +import { TRPCClientError, httpBatchLink } from '@trpc/client'; +import { createTRPCNext } from '@trpc/next'; +import superjson from 'superjson'; +import type { AppRouter } from '../server/routers/_app'; + +function getBaseUrl() { + if (typeof window !== 'undefined') return ''; + + if (process.env.VERCEL_URL) { + return `https://${process.env.VERCEL_URL}`; + } else { + return `http://localhost:${process.env.PORT ?? 3000}`; + } +} + +export const trpc = createTRPCNext({ + config({ ctx }) { + return { + transformer: superjson, + links: [ + httpBatchLink({ + url: `${getBaseUrl()}/api/trpc`, + }), + ], + }; + }, + ssr: false, +}); + +export function isTRPCClientError(error: unknown): error is TRPCClientError { + return error instanceof TRPCClientError; +} diff --git a/tests/integration/tests/trpc/test-project/next.config.js b/tests/integration/tests/trpc/test-project/next.config.js new file mode 100644 index 000000000..ae887958d --- /dev/null +++ b/tests/integration/tests/trpc/test-project/next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, +} + +module.exports = nextConfig diff --git a/tests/integration/tests/trpc/test-project/package-lock.json b/tests/integration/tests/trpc/test-project/package-lock.json new file mode 100644 index 000000000..fdff3569b --- /dev/null +++ b/tests/integration/tests/trpc/test-project/package-lock.json @@ -0,0 +1,1020 @@ +{ + "name": "test-project", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-project", + "version": "0.1.0", + "dependencies": { + "@prisma/client": "^4.7.0", + "@tanstack/react-query": "^4.22.4", + "@trpc/client": "^10.9.0", + "@trpc/next": "^10.9.0", + "@trpc/react-query": "^10.9.0", + "@trpc/server": "^10.9.0", + "@types/node": "18.11.18", + "@types/react": "18.0.27", + "@types/react-dom": "18.0.10", + "next": "13.1.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "superjson": "^1.12.2", + "typescript": "4.9.4", + "zod": "^3.20.2" + }, + "devDependencies": { + "prisma": "^4.7.0" + } + }, + "node_modules/@next/env": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz", + "integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A==" + }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz", + "integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz", + "integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz", + "integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz", + "integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-freebsd-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz", + "integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz", + "integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz", + "integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz", + "integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz", + "integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz", + "integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz", + "integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz", + "integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz", + "integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@prisma/client": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz", + "integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==", + "hasInstallScript": true, + "dependencies": { + "@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz", + "integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==", + "devOptional": true, + "hasInstallScript": true + }, + "node_modules/@prisma/engines-version": { + "version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz", + "integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA==" + }, + "node_modules/@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.4.tgz", + "integrity": "sha512-t79CMwlbBnj+yL82tEcmRN93bL4U3pae2ota4t5NN2z3cIeWw74pzdWrKRwOfTvLcd+b30tC+ciDlfYOKFPGUw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.22.4.tgz", + "integrity": "sha512-e5j5Z88XUQGeEPMyz5XF1V0mMf6Da+6URXiTpZfUb9nuHs2nlNoA+EoIvnhccE5b9YT6Yg7kARhn2L7u94M/4A==", + "dependencies": { + "@tanstack/query-core": "4.22.4", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@trpc/client": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.9.0.tgz", + "integrity": "sha512-id6318qpgqllNOuBp7nuciXFPXCe+zae5d4r1hze6Eyp5fFFNO58TqA+4Q44KIcHgpfWyW2egs6iPeql3PrdKA==", + "peerDependencies": { + "@trpc/server": "10.9.0" + } + }, + "node_modules/@trpc/next": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/next/-/next-10.9.0.tgz", + "integrity": "sha512-h/W8VHMki/fw7vHNMs1Ku7bLqccjpiKCE1wkqbvHfBOO1ARDmarRbuK6dLqJilip/f7jkFXcrxwB+eNgK4+6Wg==", + "dependencies": { + "react-ssr-prepass": "^1.5.0" + }, + "peerDependencies": { + "@tanstack/react-query": "^4.3.8", + "@trpc/client": "10.9.0", + "@trpc/react-query": "^10.8.0", + "@trpc/server": "10.9.0", + "next": "*", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@trpc/react-query": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.9.0.tgz", + "integrity": "sha512-I7xtdFj/AETwc9HfY2bGxKINij39r26LvfIiTBfz2pBj1ELrcgucWmPeI2eS6pon/uI1E9Nqa6fQCg/zkg764Q==", + "peerDependencies": { + "@tanstack/react-query": "^4.3.8", + "@trpc/client": "10.9.0", + "@trpc/server": "10.9.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@trpc/server": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.9.0.tgz", + "integrity": "sha512-5psyZgbvy29pJND32hwkWTMbv6s86IbsPOeDopsgNF0VegZT6Dsijmb7Ub/TDhuJVQVq5u4u5briMXi3SxmBkw==" + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/react": { + "version": "18.0.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/copy-anything": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", + "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/is-what": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", + "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz", + "integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==", + "dependencies": { + "@next/env": "13.1.4", + "@swc/helpers": "0.4.14", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=14.6.0" + }, + "optionalDependencies": { + "@next/swc-android-arm-eabi": "13.1.4", + "@next/swc-android-arm64": "13.1.4", + "@next/swc-darwin-arm64": "13.1.4", + "@next/swc-darwin-x64": "13.1.4", + "@next/swc-freebsd-x64": "13.1.4", + "@next/swc-linux-arm-gnueabihf": "13.1.4", + "@next/swc-linux-arm64-gnu": "13.1.4", + "@next/swc-linux-arm64-musl": "13.1.4", + "@next/swc-linux-x64-gnu": "13.1.4", + "@next/swc-linux-x64-musl": "13.1.4", + "@next/swc-win32-arm64-msvc": "13.1.4", + "@next/swc-win32-ia32-msvc": "13.1.4", + "@next/swc-win32-x64-msvc": "13.1.4" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^6.0.0 || ^7.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prisma": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz", + "integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "4.9.0" + }, + "bin": { + "prisma": "build/index.js", + "prisma2": "build/index.js" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-ssr-prepass": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-ssr-prepass/-/react-ssr-prepass-1.5.0.tgz", + "integrity": "sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/superjson": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz", + "integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/zod": { + "version": "3.20.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.20.2.tgz", + "integrity": "sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + }, + "dependencies": { + "@next/env": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz", + "integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A==" + }, + "@next/swc-android-arm-eabi": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz", + "integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz", + "integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==", + "optional": true + }, + "@next/swc-darwin-arm64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz", + "integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz", + "integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==", + "optional": true + }, + "@next/swc-freebsd-x64": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz", + "integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz", + "integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz", + "integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz", + "integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz", + "integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz", + "integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz", + "integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz", + "integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz", + "integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==", + "optional": true + }, + "@prisma/client": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz", + "integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==", + "requires": { + "@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5" + } + }, + "@prisma/engines": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz", + "integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==", + "devOptional": true + }, + "@prisma/engines-version": { + "version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz", + "integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA==" + }, + "@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@tanstack/query-core": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.4.tgz", + "integrity": "sha512-t79CMwlbBnj+yL82tEcmRN93bL4U3pae2ota4t5NN2z3cIeWw74pzdWrKRwOfTvLcd+b30tC+ciDlfYOKFPGUw==" + }, + "@tanstack/react-query": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.22.4.tgz", + "integrity": "sha512-e5j5Z88XUQGeEPMyz5XF1V0mMf6Da+6URXiTpZfUb9nuHs2nlNoA+EoIvnhccE5b9YT6Yg7kARhn2L7u94M/4A==", + "requires": { + "@tanstack/query-core": "4.22.4", + "use-sync-external-store": "^1.2.0" + } + }, + "@trpc/client": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.9.0.tgz", + "integrity": "sha512-id6318qpgqllNOuBp7nuciXFPXCe+zae5d4r1hze6Eyp5fFFNO58TqA+4Q44KIcHgpfWyW2egs6iPeql3PrdKA==", + "requires": {} + }, + "@trpc/next": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/next/-/next-10.9.0.tgz", + "integrity": "sha512-h/W8VHMki/fw7vHNMs1Ku7bLqccjpiKCE1wkqbvHfBOO1ARDmarRbuK6dLqJilip/f7jkFXcrxwB+eNgK4+6Wg==", + "requires": { + "react-ssr-prepass": "^1.5.0" + } + }, + "@trpc/react-query": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.9.0.tgz", + "integrity": "sha512-I7xtdFj/AETwc9HfY2bGxKINij39r26LvfIiTBfz2pBj1ELrcgucWmPeI2eS6pon/uI1E9Nqa6fQCg/zkg764Q==", + "requires": {} + }, + "@trpc/server": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.9.0.tgz", + "integrity": "sha512-5psyZgbvy29pJND32hwkWTMbv6s86IbsPOeDopsgNF0VegZT6Dsijmb7Ub/TDhuJVQVq5u4u5briMXi3SxmBkw==" + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/react": { + "version": "18.0.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "caniuse-lite": { + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==" + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "copy-anything": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", + "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", + "requires": { + "is-what": "^4.1.8" + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "is-what": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", + "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "next": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz", + "integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==", + "requires": { + "@next/env": "13.1.4", + "@next/swc-android-arm-eabi": "13.1.4", + "@next/swc-android-arm64": "13.1.4", + "@next/swc-darwin-arm64": "13.1.4", + "@next/swc-darwin-x64": "13.1.4", + "@next/swc-freebsd-x64": "13.1.4", + "@next/swc-linux-arm-gnueabihf": "13.1.4", + "@next/swc-linux-arm64-gnu": "13.1.4", + "@next/swc-linux-arm64-musl": "13.1.4", + "@next/swc-linux-x64-gnu": "13.1.4", + "@next/swc-linux-x64-musl": "13.1.4", + "@next/swc-win32-arm64-msvc": "13.1.4", + "@next/swc-win32-ia32-msvc": "13.1.4", + "@next/swc-win32-x64-msvc": "13.1.4", + "@swc/helpers": "0.4.14", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "prisma": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz", + "integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==", + "devOptional": true, + "requires": { + "@prisma/engines": "4.9.0" + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-ssr-prepass": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-ssr-prepass/-/react-ssr-prepass-1.5.0.tgz", + "integrity": "sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==", + "requires": {} + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "requires": { + "client-only": "0.0.1" + } + }, + "superjson": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz", + "integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==", + "requires": { + "copy-anything": "^3.0.2" + } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "zod": { + "version": "3.20.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.20.2.tgz", + "integrity": "sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==" + } + } +} diff --git a/tests/integration/tests/trpc/test-project/package.json b/tests/integration/tests/trpc/test-project/package.json new file mode 100644 index 000000000..9837167fe --- /dev/null +++ b/tests/integration/tests/trpc/test-project/package.json @@ -0,0 +1,31 @@ +{ + "name": "test-project", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@prisma/client": "^4.7.0", + "@tanstack/react-query": "^4.22.4", + "@trpc/client": "^10.9.0", + "@trpc/next": "^10.9.0", + "@trpc/react-query": "^10.9.0", + "@trpc/server": "^10.9.0", + "@types/node": "18.11.18", + "@types/react": "18.0.27", + "@types/react-dom": "18.0.10", + "next": "13.1.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "superjson": "^1.12.2", + "typescript": "4.9.4", + "zod": "^3.20.2" + }, + "devDependencies": { + "prisma": "^4.7.0" + } +} diff --git a/tests/integration/tests/trpc/test-project/pages/_app.tsx b/tests/integration/tests/trpc/test-project/pages/_app.tsx new file mode 100644 index 000000000..b51367afa --- /dev/null +++ b/tests/integration/tests/trpc/test-project/pages/_app.tsx @@ -0,0 +1,8 @@ +import type { AppProps } from 'next/app'; +import { trpc } from '../lib/trpc'; + +function App({ Component, pageProps }: AppProps) { + return ; +} + +export default trpc.withTRPC(App); diff --git a/tests/integration/tests/trpc/test-project/pages/api/model/[...path].ts b/tests/integration/tests/trpc/test-project/pages/api/model/[...path].ts new file mode 100644 index 000000000..2cc7bf38d --- /dev/null +++ b/tests/integration/tests/trpc/test-project/pages/api/model/[...path].ts @@ -0,0 +1,7 @@ +import { requestHandler } from '@zenstackhq/next'; +import { withPresets } from '@zenstackhq/runtime'; +import { prisma } from '../../../server/db'; + +export default requestHandler({ + getPrisma: (req, res) => withPresets(prisma, { user: { id: 'user1' } }), +}); diff --git a/tests/integration/tests/trpc/test-project/pages/index.tsx b/tests/integration/tests/trpc/test-project/pages/index.tsx new file mode 100644 index 000000000..c9cddb4ef --- /dev/null +++ b/tests/integration/tests/trpc/test-project/pages/index.tsx @@ -0,0 +1,12 @@ +import { trpc } from '../lib/trpc'; + +export default function Home() { + const { data: posts } = trpc.post.findMany.useQuery({}); + return ( +
+ {posts?.map((post) => ( +

{post.title}

+ ))} +
+ ); +} diff --git a/tests/integration/tests/trpc/test-project/server/context.ts b/tests/integration/tests/trpc/test-project/server/context.ts new file mode 100644 index 000000000..cf41082a4 --- /dev/null +++ b/tests/integration/tests/trpc/test-project/server/context.ts @@ -0,0 +1,12 @@ +import { type inferAsyncReturnType } from '@trpc/server'; +import { type CreateNextContextOptions } from '@trpc/server/adapters/next'; +import { withPresets } from '@zenstackhq/runtime'; +import { prisma } from './db'; + +export const createContext = async ({ req, res }: CreateNextContextOptions) => { + return { + prisma: withPresets(prisma, { user: { id: 'user1' } }), + }; +}; + +export type Context = inferAsyncReturnType; diff --git a/tests/integration/tests/trpc/test-project/server/db.ts b/tests/integration/tests/trpc/test-project/server/db.ts new file mode 100644 index 000000000..c05a1eb79 --- /dev/null +++ b/tests/integration/tests/trpc/test-project/server/db.ts @@ -0,0 +1,12 @@ +import { PrismaClient } from '@prisma/client'; + +declare global { + // eslint-disable-next-line no-var + var prisma: PrismaClient | undefined; +} + +export const prisma = global.prisma || new PrismaClient(); + +if (process.env.NODE_ENV !== 'production') { + global.prisma = prisma; +} diff --git a/tests/integration/tests/trpc/test-project/server/routers/_app.ts b/tests/integration/tests/trpc/test-project/server/routers/_app.ts new file mode 100644 index 000000000..294095222 --- /dev/null +++ b/tests/integration/tests/trpc/test-project/server/routers/_app.ts @@ -0,0 +1,12 @@ +import { createRouter } from './generated/routers'; +import { initTRPC } from '@trpc/server'; +import { type Context } from '../context'; +import superjson from 'superjson'; + +const t = initTRPC.context().create({ + transformer: superjson, +}); + +export const appRouter = createRouter(t.router, t.procedure); + +export type AppRouter = typeof appRouter; diff --git a/tests/integration/tests/trpc/test-project/todo.zmodel b/tests/integration/tests/trpc/test-project/todo.zmodel new file mode 100644 index 000000000..8468b6ef2 --- /dev/null +++ b/tests/integration/tests/trpc/test-project/todo.zmodel @@ -0,0 +1,33 @@ +datasource db { + provider = 'sqlite' + url = 'file:./dev.db' +} + +generator js { + provider = 'prisma-client-js' +} + +plugin trpc { + provider = '@zenstackhq/trpc' + output = 'server/routers/generated' +} + +model User { + id String @id + name String + posts Post[] + + @@allow('create,read', true) + @@allow('update,delete', auth() == this) +} + +model Post { + id String @id + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String? + published Boolean @default(false) + + @@allow('all', auth() == this) + @@allow('read', published) +} diff --git a/tests/integration/tests/trpc/test-project/tsconfig.json b/tests/integration/tests/trpc/test-project/tsconfig.json new file mode 100644 index 000000000..99710e857 --- /dev/null +++ b/tests/integration/tests/trpc/test-project/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/tests/integration/tests/type-coverage.test.ts b/tests/integration/tests/type-coverage.test.ts deleted file mode 100644 index df879ba96..000000000 --- a/tests/integration/tests/type-coverage.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import path from 'path'; -import { makeClient, run, setup } from './utils'; -import { Decimal } from 'decimal.js'; - -describe('Type Coverage Tests', () => { - let origDir: string; - - beforeAll(async () => { - origDir = path.resolve('.'); - await setup('./tests/type-coverage.zmodel'); - }); - - beforeEach(() => { - run('npx prisma migrate reset --schema ./zenstack/schema.prisma -f'); - }); - - afterAll(() => { - process.chdir(origDir); - }); - - it('all types', async () => { - const date = new Date(); - const data = { - string: 'string', - int: 100, - bigInt: BigInt(9007199254740991), - date, - float: 1.23, - decimal: new Decimal(1.2345), - boolean: true, - bytes: Buffer.from('hello'), - }; - await makeClient('/api/data/Foo') - .post('/') - .send({ - data, - }) - .expect(201) - .expect((resp) => { - expect(resp.body).toEqual(expect.objectContaining(data)); - }); - }); -}); diff --git a/tests/integration/tests/type-coverage.zmodel b/tests/integration/tests/type-coverage.zmodel deleted file mode 100644 index 2d44a8842..000000000 --- a/tests/integration/tests/type-coverage.zmodel +++ /dev/null @@ -1,19 +0,0 @@ -datasource db { - provider = 'sqlite' - url = 'file:./type-coverage.db' -} - -model Foo { - id String @id @default(cuid()) - - string String - int Int - bigInt BigInt - date DateTime - float Float - decimal Decimal - boolean Boolean - bytes Bytes - - @@allow('all', true) -} diff --git a/tests/integration/tests/utils.ts b/tests/integration/tests/utils.ts deleted file mode 100644 index 1f9953b9b..000000000 --- a/tests/integration/tests/utils.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { execSync } from 'child_process'; -import * as path from 'path'; -import * as fs from 'fs'; -import { createServer } from 'http'; -import { apiResolver } from 'next/dist/server/api-utils/node'; -import request from 'supertest'; -import { NextApiHandler } from 'next/types'; -import supertest from 'supertest'; -import superjson from 'superjson'; -import { registerSerializers } from '../../../packages/runtime/src/serialization-utils'; - -export function run(cmd: string) { - execSync(cmd, { - stdio: 'pipe', - encoding: 'utf-8', - env: { ...process.env, DO_NOT_TRACK: '1' }, - }); -} - -export async function setup(schemaFile: string) { - registerSerializers(); - - const origDir = path.resolve('.'); - - const workDir = path.resolve('tests/test-run'); - if (fs.existsSync(workDir)) { - fs.rmSync(workDir, { recursive: true, force: true }); - } - fs.mkdirSync(path.join(workDir, 'zenstack'), { recursive: true }); - process.chdir(workDir); - - const targetSchema = path.join(workDir, 'zenstack', 'schema.zmodel'); - fs.copyFileSync(path.join(origDir, schemaFile), targetSchema); - - // install dependencies - fs.writeFileSync('.npmrc', `cache=${origDir}/.npmcache`); - run('npm init -y'); - const dependencies = [ - 'typescript', - 'swr@1.3.0', - 'react', - 'prisma@~4.7.0', - 'zod@~3.19.0', - '../../../../packages/schema', - '../../../../packages/runtime/dist', - ]; - run(`npm i ${dependencies.join(' ')}`); - - // code generation - run(`npx zenstack generate`); - run(`npx zenstack migrate dev -n init`); - - fs.writeFileSync( - 'handler.ts', - ` - import { NextApiRequest, NextApiResponse } from 'next'; - import { type RequestHandlerOptions, requestHandler, default as service } from '@zenstackhq/runtime/server'; - - const options: RequestHandlerOptions = { - async getServerUser(req: NextApiRequest, res: NextApiResponse) { - if (req.cookies.userId === '') { - // simulate "undefined" user id - return {} as { id: string}; - } else if (req.cookies.userId) { - return { id: req.cookies.userId }; - } else { - return undefined; - } - }, - }; - export default requestHandler(service, options); - ` - ); - - fs.copyFileSync( - path.join(origDir, 'tests/tsconfig.template.json'), - path.join(workDir, 'tsconfig.json') - ); - run('npx tsc'); - - return workDir; -} - -export function makeClient(apiPath: string, userId?: string, queryArgs?: any) { - const [api, ...pathParts] = apiPath.split('/').filter((p) => p); - if (api !== 'api') { - throw new Error('apiPath must start with /api'); - } - - const query = { - path: pathParts, - ...(queryArgs ? { q: superjson.stringify(queryArgs) } : {}), - }; - const testClient = (handler: NextApiHandler) => - request( - createServer(async (req, res) => { - return apiResolver( - req, - res, - query, - handler, - { - previewModeEncryptionKey: '', - previewModeId: '', - previewModeSigningKey: '', - }, - false - ); - }) - ); - const handler = require(path.resolve('handler.js')); - const client = testClient(handler); - - // proxy test and superjson marshal post data - const proxyTest = (test: supertest.Test) => { - // return test; - return new Proxy(test, { - get(target: supertest.Test, prop: string | symbol, receiver: any) { - if (prop === 'send') { - return (data: any) => { - return target.send( - JSON.parse(superjson.stringify(data)) - ); - }; - } else { - return Reflect.get(target, prop, receiver); - } - }, - }); - }; - - const proxied = new Proxy(client, { - get( - target: supertest.SuperTest, - prop: string | symbol, - receiver: any - ) { - switch (prop) { - case 'get': - case 'post': - case 'put': - case 'del': - case 'delete': - return (url: string) => { - const test = target[prop](url) as supertest.Test; - - // use userId cookie to simulate a logged in user - if (userId) { - test.set('Cookie', [`userId=${userId}`]); - } - - test.expect((resp) => { - if ( - (resp.status === 200 || resp.status === 201) && - resp.body - ) { - // unmarshal response - resp.body = superjson.parse( - JSON.stringify(resp.body) - ); - } - }); - - return proxyTest(test); - }; - - default: - return Reflect.get(target, prop, receiver); - } - }, - }); - - return proxied; -} diff --git a/tests/integration/tests/with-omit/with-omit.test.ts b/tests/integration/tests/with-omit/with-omit.test.ts new file mode 100644 index 000000000..57e69128c --- /dev/null +++ b/tests/integration/tests/with-omit/with-omit.test.ts @@ -0,0 +1,82 @@ +import { MODEL_PRELUDE, loadPrisma } from '../../utils/utils'; +import path from 'path'; + +describe('Omit test', () => { + let origDir: string; + const suite = 'omit'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(async () => { + process.chdir(origDir); + }); + + it('omit tests', async () => { + const { withOmit } = await loadPrisma( + `${suite}/test`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(cuid()) + password String @omit + profile Profile? + + @@allow('all', true) + } + + model Profile { + id String @id @default(cuid()) + user User @relation(fields: [userId], references: [id]) + userId String @unique + image String @omit + + @@allow('all', true) + } ` + ); + + const db = withOmit(); + const r = await db.user.create({ + include: { profile: true }, + data: { + id: '1', + password: 'abc123', + profile: { + create: { + image: 'an image', + }, + }, + }, + }); + expect(r.password).toBeUndefined(); + expect(r.profile.image).toBeUndefined(); + + const r1 = await db.user.findUnique({ + where: { id: '1' }, + include: { profile: true }, + }); + expect(r1.password).toBeUndefined(); + expect(r1.profile.image).toBeUndefined(); + + await db.user.create({ + include: { profile: true }, + data: { + id: '2', + password: 'abc234', + profile: { + create: { + image: 'another image', + }, + }, + }, + }); + + const r2 = await db.user.findMany({ include: { profile: true } }); + r2.forEach((e: any) => { + expect(e.password).toBeUndefined(); + expect(e.profile.image).toBeUndefined(); + }); + }); +}); diff --git a/tests/integration/tests/with-password/with-password.test.ts b/tests/integration/tests/with-password/with-password.test.ts new file mode 100644 index 000000000..1e74bc700 --- /dev/null +++ b/tests/integration/tests/with-password/with-password.test.ts @@ -0,0 +1,48 @@ +import { compareSync } from 'bcryptjs'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils/utils'; +import path from 'path'; + +describe('Password test', () => { + let origDir: string; + const suite = 'password'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(async () => { + process.chdir(origDir); + }); + + it('password tests', async () => { + const { withPassword } = await loadPrisma( + `${suite}/test`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(cuid()) + password String @password(saltLength: 16) + + @@allow('all', true) + }` + ); + + const db = withPassword(); + const r = await db.user.create({ + data: { + id: '1', + password: 'abc123', + }, + }); + expect(compareSync('abc123', r.password)).toBeTruthy(); + + const r1 = await db.user.update({ + where: { id: '1' }, + data: { + password: 'abc456', + }, + }); + expect(compareSync('abc456', r1.password)).toBeTruthy(); + }); +}); diff --git a/tests/integration/tests/with-policy/auth.test.ts b/tests/integration/tests/with-policy/auth.test.ts new file mode 100644 index 000000000..2987a156f --- /dev/null +++ b/tests/integration/tests/with-policy/auth.test.ts @@ -0,0 +1,213 @@ +import { PrismaClientValidationError } from '@prisma/client/runtime'; +import path from 'path'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils'; + +describe('With Policy:undefined user', () => { + let origDir: string; + const suite = 'undefined-user'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('undefined user with string id', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user with string id`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(uuid()) + } + + model Post { + id String @id @default(uuid()) + title String + + @@allow('read', true) + @@allow('create', auth() != null) + } + ` + ); + + const db = withPolicy(); + await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); + + const authDb = withPolicy({ id: 'user1' }); + await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); + }); + + it('undefined user with string id more', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user with string id more`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(uuid()) + } + + model Post { + id String @id @default(uuid()) + title String + + @@allow('read', true) + @@allow('create', auth().id != null) + } + ` + ); + + const db = withPolicy(); + await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); + + const authDb = withPolicy({ id: 'user1' }); + await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); + }); + + it('undefined user with int id', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user with int id`, + ` + ${MODEL_PRELUDE} + + model User { + id Int @id @default(autoincrement()) + } + + model Post { + id String @id @default(uuid()) + title String + + @@allow('read', true) + @@allow('create', auth() != null) + } + ` + ); + + const db = withPolicy(); + await expect(db.post.create({ data: { title: 'abc' } })).toBeRejectedByPolicy(); + + const authDb = withPolicy({ id: 'user1' }); + await expect(authDb.post.create({ data: { title: 'abc' } })).toResolveTruthy(); + }); + + it('undefined user compared with field', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user compared with field`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(uuid()) + posts Post[] + + @@allow('all', true) + } + + model Post { + id String @id @default(uuid()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId String + + @@allow('create,read', true) + @@allow('update', auth() == author) + } + ` + ); + + const db = withPolicy(); + await expect(db.user.create({ data: { id: 'user1' } })).toResolveTruthy(); + await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); + + const authDb = withPolicy(); + await expect(authDb.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); + + const authDb1 = withPolicy({ id: null }); + await expect(authDb1.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).rejects.toThrow(); + + const authDb2 = withPolicy({ id: 'user1' }); + await expect(authDb2.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); + }); + + it('undefined user compared with field more', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user compared with field more`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(uuid()) + posts Post[] + + @@allow('all', true) + } + + model Post { + id String @id @default(uuid()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId String + + @@allow('create,read', true) + @@allow('update', auth().id == author.id) + } + ` + ); + + const db = withPolicy(); + await expect(db.user.create({ data: { id: 'user1' } })).toResolveTruthy(); + await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); + + await expect(db.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); + + const authDb1 = withPolicy({ id: null }); + await expect(authDb1.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).rejects.toThrow(); + + const authDb2 = withPolicy({ id: 'user1' }); + await expect(authDb2.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); + }); + + it('undefined user non-id field', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/undefined user non-id field`, + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(uuid()) + posts Post[] + role String + + @@allow('all', true) + } + + model Post { + id String @id @default(uuid()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId String + + @@allow('create,read', true) + @@allow('update', auth().role == 'ADMIN') + } + ` + ); + + const db = withPolicy(); + await expect(db.user.create({ data: { id: 'user1', role: 'USER' } })).toResolveTruthy(); + await expect(db.post.create({ data: { id: '1', title: 'abc', authorId: 'user1' } })).toResolveTruthy(); + + await expect(db.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); + + const authDb = withPolicy({ role: 'USER' }); + await expect(db.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toBeRejectedByPolicy(); + + const authDb1 = withPolicy({ role: 'ADMIN' }); + await expect(authDb1.post.update({ where: { id: '1' }, data: { title: 'bcd' } })).toResolveTruthy(); + }); +}); diff --git a/tests/integration/tests/with-policy/deep-nested.test.ts b/tests/integration/tests/with-policy/deep-nested.test.ts new file mode 100644 index 000000000..7cda78959 --- /dev/null +++ b/tests/integration/tests/with-policy/deep-nested.test.ts @@ -0,0 +1,434 @@ +import path from 'path'; +import { MODEL_PRELUDE, WeakDbClientContract, loadPrisma } from '../../utils'; + +describe('With Policy:deep nested', () => { + let origDir: string; + const suite = 'deep-nested'; + + const model = ` + ${MODEL_PRELUDE} + + // M1 - M2 - M3 + // -* M4 + model M1 { + myId String @id @default(cuid()) + m2 M2? + + @@allow('all', true) + @@deny('create', m2.m4?[value == 100]) + @@deny('update', m2.m4?[value == 101]) + } + + model M2 { + id Int @id @default(autoincrement()) + value Int + m1 M1 @relation(fields: [m1Id], references: [myId], onDelete: Cascade) + m1Id String @unique + + m3 M3? + m4 M4[] + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + @@allow('delete', value > 2) + } + + model M3 { + id String @id @default(cuid()) + value Int + m2 M2 @relation(fields: [m2Id], references: [id], onDelete: Cascade) + m2Id Int @unique + + @@allow('read', true) + @@allow('create', value > 10) + @@allow('update', value > 1) + @@allow('delete', value > 2) + @@deny('read', value == 200) + } + + model M4 { + id String @id @default(cuid()) + value Int + m2 M2? @relation(fields: [m2Id], references: [id], onDelete: Cascade) + m2Id Int? + + @@allow('read', true) + @@allow('create', value > 20) + @@allow('update', value > 21) + @@allow('delete', value > 22) + @@deny('read', value == 200) + } + `; + + let db: WeakDbClientContract; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + beforeEach(async () => { + const { withPolicy } = await loadPrisma(`${suite}/shared`, model); + db = withPolicy(); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('create', async () => { + await expect( + db.m1.create({ + data: { + myId: '1', + m2: { + create: { + id: 201, + value: 1, + m3: { + create: { + id: 'm3-1', + value: 11, + }, + }, + m4: { + create: [ + { id: 'm4-1', value: 21 }, + { id: 'm4-2', value: 22 }, + ], + }, + }, + }, + }, + }) + ).toResolveTruthy(); + + const r = await db.m1.create({ + include: { m2: { include: { m3: true, m4: true } } }, + data: { + myId: '2', + m2: { + create: { + value: 2, + m3: { + connect: { + id: 'm3-1', + }, + }, + m4: { + connect: [{ id: 'm4-1' }], + connectOrCreate: [ + { + where: { id: 'm4-2' }, + create: { id: 'm4-new', value: 22 }, + }, + { + where: { id: 'm4-3' }, + create: { id: 'm4-3', value: 23 }, + }, + ], + }, + }, + }, + }, + }); + expect(r.m2.m3.id).toBe('m3-1'); + expect(r.m2.m4[0].id).toBe('m4-1'); + expect(r.m2.m4[1].id).toBe('m4-2'); + expect(r.m2.m4[2].id).toBe('m4-3'); + + // deep create violation + await expect( + db.m1.create({ + data: { + m2: { + create: { + value: 1, + m4: { + create: [{ value: 20 }, { value: 22 }], + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // deep create violation due to deep policy + await expect( + db.m1.create({ + data: { + m2: { + create: { + value: 1, + m4: { + create: { value: 100 }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // deep connect violation via deep policy: @@deny('create', m2.m4?[value == 100]) + await db.m4.create({ + data: { + id: 'm4-value-100', + value: 100, + }, + }); + await expect( + db.m1.create({ + data: { + m2: { + create: { + value: 1, + m4: { + connect: { id: 'm4-value-100' }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // create read-back filter: M4 @@deny('read', value == 200) + const r1 = await db.m1.create({ + include: { m2: { include: { m4: true } } }, + data: { + m2: { + create: { + value: 1, + m4: { + create: [{ value: 200 }, { value: 201 }], + }, + }, + }, + }, + }); + expect(r1.m2.m4).toHaveLength(1); + + // create read-back rejection: M3 @@deny('read', value == 200) + await expect( + db.m1.create({ + include: { m2: { include: { m3: true } } }, + data: { + m2: { + create: { + value: 1, + m3: { + create: { value: 200 }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('update', async () => { + await db.m1.create({ + data: { myId: '1' }, + }); + + // success + await expect( + db.m1.update({ + where: { myId: '1' }, + include: { m2: { include: { m3: true, m4: true } } }, + data: { + m2: { + create: { + id: 201, + value: 2, + m3: { + create: { id: 'm3-1', value: 11 }, + }, + m4: { + create: [ + { id: 'm4-1', value: 22 }, + { id: 'm4-2', value: 23 }, + ], + }, + }, + }, + }, + }) + ).toResolveTruthy(); + + // deep update with connect/disconnect/delete success + await db.m4.create({ + data: { + id: 'm4-3', + value: 23, + }, + }); + const r = await db.m1.update({ + where: { myId: '1' }, + include: { m2: { include: { m4: true } } }, + data: { + m2: { + update: { + m4: { + connect: [{ id: 'm4-3' }], + disconnect: { id: 'm4-1' }, + delete: { id: 'm4-2' }, + }, + }, + }, + }, + }); + expect(r.m2.m4).toHaveLength(1); + expect(r.m2.m4[0].id).toBe('m4-3'); + + // reconnect m14-1, create m14-2 + await expect( + db.m1.update({ + where: { myId: '1' }, + include: { m2: { include: { m4: true } } }, + data: { + m2: { + update: { + m4: { + connect: [{ id: 'm4-1' }], + create: { id: 'm4-2', value: 23 }, + }, + }, + }, + }, + }) + ).toResolveTruthy(); + + // deep update violation + await expect( + db.m1.update({ + where: { myId: '1' }, + data: { + m2: { + update: { + m4: { + create: { value: 20 }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // deep update violation via deep policy: @@deny('update', m2.m4?[value == 101]) + await db.m1.create({ + data: { + myId: '2', + m2: { + create: { + value: 2, + m4: { + create: { id: 'm4-101', value: 101 }, + }, + }, + }, + }, + }); + await expect( + db.m1.update({ + where: { myId: '2' }, + data: { + m2: { + update: { + m4: { + updateMany: { + where: { value: { gt: 0 } }, + data: { value: 102 }, + }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // update read-back filter: M4 @@deny('read', value == 200) + const r1 = await db.m1.update({ + where: { myId: '1' }, + include: { m2: { include: { m4: true } } }, + data: { + m2: { + update: { + m4: { + update: { + where: { id: 'm4-1' }, + data: { value: 200 }, + }, + }, + }, + }, + }, + }); + expect(r1.m2.m4).toHaveLength(2); + expect(r1.m2.m4).not.toContain(expect.objectContaining({ id: 'm4-1' })); + + // update read-back rejection: M3 @@deny('read', value == 200) + await expect( + db.m1.update({ + where: { myId: '1' }, + include: { m2: { include: { m3: true } } }, + data: { + m2: { + update: { + m3: { + update: { value: 200 }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('delete', async () => { + await db.m1.create({ + data: { + myId: '1', + m2: { + create: { + value: 1, + m4: { + create: [{ value: 200 }, { value: 22 }], + }, + }, + }, + }, + }); + + // delete read-back reject: M4 @@deny('read', value == 200) + await expect( + db.m1.delete({ + where: { myId: '1' }, + include: { m2: { select: { m4: true } } }, + }) + ).toBeRejectedByPolicy(['result not readable']); + + await expect(db.m4.findMany()).resolves.toHaveLength(0); + + await db.m1.create({ + data: { + myId: '2', + m2: { + create: { + value: 1, + m3: { + create: { value: 200 }, + }, + }, + }, + }, + }); + + // delete read-back reject: M3 @@deny('read', value == 200) + await expect( + db.m1.delete({ + where: { myId: '2' }, + include: { m2: { select: { m3: { select: { id: true } } } } }, + }) + ).toBeRejectedByPolicy(); + }); +}); diff --git a/tests/integration/tests/with-policy/empty-policy.test.ts b/tests/integration/tests/with-policy/empty-policy.test.ts new file mode 100644 index 000000000..e540213f7 --- /dev/null +++ b/tests/integration/tests/with-policy/empty-policy.test.ts @@ -0,0 +1,126 @@ +import path from 'path'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils'; + +describe('With Policy:empty policy', () => { + let origDir: string; + const suite = 'empty-policy'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('direct operations', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/direct operations`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + } + ` + ); + + const db = withPolicy(); + + await expect(db.model.create({ data: {} })).toBeRejectedByPolicy(); + + expect(await db.model.findMany()).toHaveLength(0); + expect(await db.model.findUnique({ where: { id: '1' } })).toBeNull(); + expect(await db.model.findFirst({ where: { id: '1' } })).toBeNull(); + await expect(db.model.findUniqueOrThrow({ where: { id: '1' } })).toBeNotFound(); + await expect(db.model.findFirstOrThrow({ where: { id: '1' } })).toBeNotFound(); + + await expect(db.model.create({ data: {} })).toBeRejectedByPolicy(); + await expect(db.model.createMany({ data: [{}] })).toBeRejectedByPolicy(); + + await expect(db.model.update({ where: { id: '1' }, data: {} })).toBeRejectedByPolicy(); + await expect(db.model.updateMany({ data: {} })).toBeRejectedByPolicy(); + await expect( + db.model.upsert({ + where: { id: '1' }, + create: {}, + update: {}, + }) + ).toBeRejectedByPolicy(); + + await expect(db.model.delete({ where: { id: '1' } })).toBeRejectedByPolicy(); + await expect(db.model.deleteMany()).toBeRejectedByPolicy(); + + await expect(db.model.aggregate({})).toBeRejectedByPolicy(); + await expect(db.model.groupBy({})).toBeRejectedByPolicy(); + await expect(db.model.count()).toBeRejectedByPolicy(); + }); + + it('to-many write', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/to-many write`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + m2: { + create: [{}], + }, + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('to-one write', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested write to-one`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + m2: { + create: {}, + }, + }, + }) + ).toBeRejectedByPolicy(); + }); +}); diff --git a/tests/integration/tests/with-policy/field-validation.test.ts b/tests/integration/tests/with-policy/field-validation.test.ts new file mode 100644 index 000000000..aeaf218e3 --- /dev/null +++ b/tests/integration/tests/with-policy/field-validation.test.ts @@ -0,0 +1,461 @@ +import { MODEL_PRELUDE, WeakDbClientContract, loadPrisma, run } from '../../utils'; + +describe('With Policy: field validation', () => { + let db: WeakDbClientContract; + let prisma: WeakDbClientContract; + + beforeAll(async () => { + const { withPolicy, prisma: _prisma } = await loadPrisma( + 'field-validation', + ` + ${MODEL_PRELUDE} + + model User { + id String @id @default(cuid()) + password String @length(8, 16) + email String? @email @endsWith("@myorg.com") + profileImage String? @url + handle String? @regex("^[0-9a-zA-Z]{4,16}$") + + userData UserData? + tasks Task[] + + @@allow('all', true) + } + + model UserData { + id String @id @default(cuid()) + user User @relation(fields: [userId], references: [id]) + userId String @unique + + a Int @gt(0) + b Int @gte(0) + c Int @lt(0) + d Int @lte(0) + text1 String @startsWith('abc') + text2 String @endsWith('def') + text3 String @length(min: 3) + text4 String @length(max: 5) + text5 String? @endsWith('xyz') + + @@allow('all', true) + } + + model Task { + id String @id @default(cuid()) + user User @relation(fields: [userId], references: [id]) + userId String + slug String @regex("^[0-9a-zA-Z]{4,16}$") + + @@allow('all', true) + } +` + ); + db = withPolicy(); + prisma = _prisma; + }); + + beforeEach(() => { + run('npx prisma migrate reset --force'); + run('npx prisma db push'); + }); + + it('direct write simple', async () => { + await expect( + db.user.create({ + data: { + id: '1', + password: 'abc123', + handle: 'hello world', + }, + }) + ).toBeRejectedByPolicy(['String must contain at least 8 character(s) at "password"', 'Invalid at "handle"']); + + await expect( + db.user.create({ + data: { + id: '1', + password: 'abc123!@#', + email: 'something', + handle: 'user1user1user1user1user1', + }, + }) + ).toBeRejectedByPolicy([ + 'Invalid email at "email"', + 'must end with "@myorg.com" at "email"', + 'Invalid at "handle"', + ]); + + await expect( + db.user.create({ + data: { + id: '1', + password: 'abc123!@#', + email: 'who@myorg.com', + handle: 'user1', + }, + }) + ).toResolveTruthy(); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + password: 'abc123', + email: 'something', + }, + }) + ).toBeRejectedByPolicy([ + 'String must contain at least 8 character(s) at "password"', + 'must end with "@myorg.com" at "email"', + ]); + }); + + it('direct write more', async () => { + await db.user.create({ + data: { + id: '1', + password: 'abc123!@#', + email: 'who@myorg.com', + handle: 'user1', + }, + }); + + await expect( + db.userData.create({ + data: { + userId: '1', + a: 0, + b: -1, + c: 0, + d: 1, + text1: 'a', + text2: 'xyz', + text3: 'a', + text4: 'abcabc', + text5: 'abc', + }, + }) + ).toBeRejectedByPolicy([ + 'Number must be greater than 0 at "a"', + 'Number must be greater than or equal to 0 at "b"', + 'Number must be less than 0 at "c"', + 'Number must be less than or equal to 0 at "d"', + 'must start with "abc" at "text1"', + 'must end with "def" at "text2"', + 'String must contain at least 3 character(s) at "text3"', + 'String must contain at most 5 character(s) at "text4"', + 'must end with "xyz" at "text5"', + ]); + + await expect( + db.userData.create({ + data: { + userId: '1', + a: 1, + b: 0, + c: -1, + d: 0, + text1: 'abc123', + text2: 'def', + text3: 'aaa', + text4: 'abcab', + }, + }) + ).toResolveTruthy(); + }); + + it('nested create test', async () => { + const user = { + password: 'abc123!@#', + email: 'who@myorg.com', + handle: 'user1', + }; + + const userData = { + a: 1, + b: 0, + c: -1, + d: 0, + text1: 'abc123', + text2: 'def', + text3: 'aaa', + text4: 'abcab', + }; + + const tasks = [ + { + slug: 'abcabc', + }, + { + slug: 'abcdef', + }, + ]; + + await expect( + db.user.create({ + data: { + ...user, + userData: { + create: { + ...userData, + a: 0, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Number must be greater than 0 at "a"']); + + await expect( + db.user.create({ + data: { + ...user, + tasks: { + create: { + slug: 'abc', + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Invalid at "slug"']); + + await expect( + db.user.create({ + data: { + ...user, + userData: { + connectOrCreate: { + where: { + id: '1', + }, + create: { + ...userData, + a: 0, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Number must be greater than 0 at "a"']); + + await expect( + db.user.create({ + data: { + ...user, + tasks: { + create: [ + { + slug: 'abc', + }, + { + slug: 'abcdef', + }, + ], + }, + }, + }) + ).toBeRejectedByPolicy(['Invalid at "slug"']); + + await expect( + db.user.create({ + data: { + ...user, + userData: { + connectOrCreate: { + where: { + id: '1', + }, + create: userData, + }, + }, + tasks: { + create: tasks, + }, + }, + }) + ).toResolveTruthy(); + }); + + it('nested update test', async () => { + const user = { + password: 'abc123!@#', + email: 'who@myorg.com', + handle: 'user1', + }; + + const userData = { + a: 1, + b: 0, + c: -1, + d: 0, + text1: 'abc123', + text2: 'def', + text3: 'aaa', + text4: 'abcab', + }; + + await expect( + db.user.create({ + data: { + id: '1', + ...user, + }, + }) + ).toResolveTruthy(); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + create: { + ...userData, + a: 0, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Number must be greater than 0 at "a"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + tasks: { + create: { + slug: 'abc', + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Invalid at "slug"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + create: { id: '1', ...userData }, + }, + tasks: { + create: { + id: '1', + slug: 'abcabc', + }, + }, + }, + }) + ).toResolveTruthy(); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + update: { + a: 0, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Number must be greater than 0 at "a"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + tasks: { + update: { + where: { id: '1' }, + data: { + slug: 'abc', + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Invalid at "slug"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + update: { + a: 2, + }, + }, + tasks: { + update: { + where: { id: '1' }, + data: { + slug: 'defdef', + }, + }, + }, + }, + }) + ).toResolveTruthy(); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + upsert: { + create: { + ...userData, + a: 0, + }, + update: { + a: 0, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Number must be greater than 0 at "a"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + tasks: { + updateMany: { + where: { id: '1' }, + data: { + slug: 'abc', + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(['Invalid at "slug"']); + + await expect( + db.user.update({ + where: { id: '1' }, + data: { + userData: { + upsert: { + create: { + ...userData, + }, + update: { + a: 1, + }, + }, + }, + tasks: { + updateMany: { + where: { id: '1' }, + data: { + slug: 'xxxyyy', + }, + }, + }, + }, + }) + ).toResolveTruthy(); + }); +}); diff --git a/tests/integration/tests/with-policy/nested-to-many.test.ts b/tests/integration/tests/with-policy/nested-to-many.test.ts new file mode 100644 index 000000000..eb27961da --- /dev/null +++ b/tests/integration/tests/with-policy/nested-to-many.test.ts @@ -0,0 +1,511 @@ +import path from 'path'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils'; + +describe('With Policy:nested to-many', () => { + let origDir: string; + const suite = 'nested-to-many'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('create simple', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/create`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('read', true) + @@allow('create', value > 0) + } + ` + ); + + const db = withPolicy(); + + // single create denied + await expect( + db.m1.create({ + data: { + m2: { + create: { value: 0 }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.create({ + data: { + m2: { + create: { value: 1 }, + }, + }, + }) + ).toResolveTruthy(); + + // multi create denied + await expect( + db.m1.create({ + data: { + m2: { + create: [{ value: 0 }, { value: 1 }], + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.create({ + data: { + m2: { + create: [{ value: 1 }, { value: 2 }], + }, + }, + }) + ).toResolveTruthy(); + }); + + it('update simple', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/update`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('read', true) + @@allow('create', true) + @@allow('update', value > 1) + } + ` + ); + + const db = withPolicy(); + + await db.m1.create({ + data: { + id: '1', + m2: { + create: [{ id: '1', value: 1 }], + }, + }, + }); + + // update denied + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + update: { + where: { id: '1' }, + data: { value: 2 }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await db.m1.create({ + data: { + id: '2', + m2: { + create: { id: '2', value: 2 }, + }, + }, + }); + + // update success + const r = await db.m1.update({ + where: { id: '2' }, + include: { m2: true }, + data: { + m2: { + update: { + where: { id: '2' }, + data: { value: 3 }, + }, + }, + }, + }); + expect(r.m2).toEqual(expect.arrayContaining([expect.objectContaining({ id: '2', value: 3 })])); + }); + + it('update with create', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/update with create`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + } + ` + ); + + const db = withPolicy(); + + await db.m1.create({ + data: { + id: '1', + m2: { + create: { value: 1 }, + }, + }, + }); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + create: [{ value: 0 }, { value: 1 }], + }, + }, + }) + ).toBeRejectedByPolicy(); + + const r = await db.m1.update({ + where: { id: '1' }, + include: { m2: true }, + data: { + m2: { + create: [{ value: 1 }, { value: 2 }], + }, + }, + }); + expect(r.m2).toHaveLength(3); + }); + + it('update with delete', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/update with delete`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + @@allow('delete', value > 2) + } + ` + ); + + const db = withPolicy(); + + await db.m1.create({ + data: { + id: '1', + m2: { + create: [ + { id: '1', value: 1 }, + { id: '2', value: 2 }, + { id: '3', value: 3 }, + { id: '4', value: 4 }, + { id: '5', value: 5 }, + ], + }, + }, + }); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + delete: { id: '1' }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + deleteMany: { OR: [{ id: '2' }, { id: '3' }] }, + }, + }, + }) + ).toResolveTruthy(); + // only m2#3 should be deleted, m2#2 should remain because of policy + await expect(db.m2.findUnique({ where: { id: '3' } })).toResolveNull(); + await expect(db.m2.findUnique({ where: { id: '2' } })).toResolveTruthy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + delete: { id: '3' }, + }, + }, + }) + ).toBeRejectedWithCode('P2017'); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + deleteMany: { value: { gte: 4 } }, + }, + }, + }) + ).toResolveTruthy(); + + await expect(db.m2.findMany({ where: { id: { in: ['4', '5'] } } })).resolves.toHaveLength(0); + }); + + it('create with nested read', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/create with nested read`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + value Int + m2 M2[] + m3 M3? + + @@allow('read', value > 1) + @@allow('create', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('create', true) + @@allow('read', value > 0) + } + + model M3 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('create', true) + @@allow('read', value > 0) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + value: 1, + }, + }) + ).toBeRejectedByPolicy(); + + // included 'm1' can't be read + await expect( + db.m2.create({ + include: { m1: true }, + data: { + id: '1', + value: 1, + m1: { connect: { id: '1' } }, + }, + }) + ).toBeRejectedByPolicy(); + await expect(db.m2.findUnique({ where: { id: '1' } })).toResolveTruthy(); + + // included 'm1' can't be read + await expect( + db.m3.create({ + include: { m1: true }, + data: { + id: '1', + value: 1, + m1: { connect: { id: '1' } }, + }, + }) + ).toBeRejectedByPolicy(); + await expect(db.m3.findUnique({ where: { id: '1' } })).toResolveTruthy(); + + // nested to-many got filtered on read + const r = await db.m1.create({ + include: { m2: true }, + data: { + value: 2, + m2: { create: [{ value: 0 }, { value: 1 }] }, + }, + }); + expect(r.m2).toHaveLength(1); + + // read-back for to-one relation rejected + await expect( + db.m1.create({ + include: { m3: true }, + data: { + value: 2, + m3: { create: { value: 0 } }, + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('update with nested read', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/update with nested read`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + m3 M3? + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('read', value > 1) + @@allow('create,update', true) + } + + model M3 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('read', value > 1) + @@allow('create,update', true) + } + ` + ); + + const db = withPolicy(); + await db.m1.create({ + data: { + id: '1', + m2: { + create: [ + { id: '1', value: 0 }, + { id: '2', value: 0 }, + ], + }, + m3: { + create: { value: 0 }, + }, + }, + }); + + await expect( + db.m1.update({ + where: { id: '1' }, + include: { m3: true }, + data: { + m3: { + update: { + value: 1, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + const r = await db.m1.update({ + where: { id: '1' }, + include: { m3: true, m2: true }, + data: { + m3: { + update: { + value: 2, + }, + }, + }, + }); + // m3 is ok now + expect(r.m3.value).toBe(2); + // m2 got filtered + expect(r.m2).toHaveLength(0); + + const r1 = await db.m1.update({ + where: { id: '1' }, + select: { m2: true }, + data: { + m2: { + update: { + where: { id: '1' }, + data: { value: 2 }, + }, + }, + }, + }); + // one of m2 matches policy now + expect(r1.m2).toHaveLength(1); + }); +}); diff --git a/tests/integration/tests/with-policy/nested-to-one.test.ts b/tests/integration/tests/with-policy/nested-to-one.test.ts new file mode 100644 index 000000000..5ce5fcb6e --- /dev/null +++ b/tests/integration/tests/with-policy/nested-to-one.test.ts @@ -0,0 +1,205 @@ +import path from 'path'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils'; + +describe('With Policy:nested to-one', () => { + let origDir: string; + const suite = 'nested-to-one'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('create and update tests', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/create and update`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + } + ` + ); + + const db = withPolicy(); + + // create denied + await expect( + db.m1.create({ + data: { + m2: { + create: { value: 0 }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + m2: { + create: { id: '1', value: 1 }, + }, + }, + }) + ).toResolveTruthy(); + + // nested update denied + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + update: { value: 2 }, + }, + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('nested create', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested create`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + } + ` + ); + + const db = withPolicy(); + + await db.m1.create({ + data: { + id: '1', + }, + }); + + // nested create denied + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + create: { value: 0 }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { + create: { value: 1 }, + }, + }, + }) + ).toResolveTruthy(); + }); + + it('nested delete', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested delete`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('read', true) + @@allow('create', true) + @@allow('update', true) + @@allow('delete', value > 1) + } + ` + ); + + const db = withPolicy(); + + await db.m1.create({ + data: { + id: '1', + m2: { + create: { id: '1', value: 1 }, + }, + }, + }); + + // nested delete denied + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { delete: true }, + }, + }) + ).toBeRejectedByPolicy(); + expect(await db.m2.findUnique({ where: { id: '1' } })).toBeTruthy(); + + // update m2 so it can be deleted + await db.m1.update({ + where: { id: '1' }, + data: { + m2: { update: { value: 3 } }, + }, + }); + + expect( + await db.m1.update({ + where: { id: '1' }, + data: { + m2: { delete: true }, + }, + }) + ).toBeTruthy(); + // check deleted + expect(await db.m2.findUnique({ where: { id: '1' } })).toBeNull(); + }); +}); diff --git a/tests/integration/tests/with-policy/post-update.test.ts b/tests/integration/tests/with-policy/post-update.test.ts new file mode 100644 index 000000000..2919c24da --- /dev/null +++ b/tests/integration/tests/with-policy/post-update.test.ts @@ -0,0 +1,351 @@ +import path from 'path'; +import { MODEL_PRELUDE, loadPrisma } from '../../utils'; + +describe('With Policy: post update', () => { + let origDir: string; + const suite = 'post-update'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(() => { + process.chdir(origDir); + }); + + it('simple allow', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/simple-allow`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('create,read', true) + @@allow('update', future().value > 1) + } + ` + ); + + const db = withPolicy(); + + await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); + await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); + await expect(db.model.update({ where: { id: '1' }, data: { value: 2 } })).toResolveTruthy(); + }); + + it('simple deny', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/simple-deny`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('all', true) + @@deny('update', future().value <= 1) + } + ` + ); + + const db = withPolicy(); + + await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); + await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); + await expect(db.model.update({ where: { id: '1' }, data: { value: 2 } })).toResolveTruthy(); + }); + + it('mixed pre and post', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/mixed`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('create,read', true) + @@allow('update', value > 0 && future().value > value) + } + ` + ); + + const db = withPolicy(); + + await expect(db.model.create({ data: { id: '1', value: 0 } })).toResolveTruthy(); + await expect(db.model.update({ where: { id: '1' }, data: { value: 1 } })).toBeRejectedByPolicy(); + + await expect(db.model.create({ data: { id: '2', value: 3 } })).toResolveTruthy(); + await expect(db.model.update({ where: { id: '2' }, data: { value: 2 } })).toBeRejectedByPolicy(); + await expect(db.model.update({ where: { id: '2' }, data: { value: 4 } })).toResolveTruthy(); + }); + + it('nested to-many', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested-to-many`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2[] + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String + + @@allow('create,read', true) + @@allow('update', future().value > 1) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + m2: { + create: [ + { id: '1', value: 0 }, + { id: '2', value: 1 }, + ], + }, + }, + }) + ).toResolveTruthy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { updateMany: { where: {}, data: { value: { increment: 1 } } } } }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { updateMany: { where: { value: { gte: 1 } }, data: { value: { increment: 1 } } } } }, + }) + ).toResolveTruthy(); + + await expect(db.m2.findMany()).resolves.toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: '1', value: 0 }), + expect.objectContaining({ id: '2', value: 2 }), + ]) + ); + }); + + it('nested to-one', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested-to-one`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('create,read', true) + @@allow('update', future().value > 1) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + m2: { + create: { id: '1', value: 0 }, + }, + }, + }) + ).toResolveTruthy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { update: { value: { increment: 1 } } } }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { update: { value: { increment: 2 } } } }, + }) + ).toResolveTruthy(); + + await expect(db.m2.findMany()).resolves.toEqual( + expect.arrayContaining([expect.objectContaining({ value: 2 })]) + ); + }); + + it('nested select', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/nested-select`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + @@allow('create,read', true) + @@allow('update', future().m2.value > m2.value) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + + @@allow('all', true) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + m2: { + create: { id: '1', value: 1 }, + }, + }, + }) + ).toResolveTruthy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { update: { value: 0 } } }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { m2: { update: { value: 2 } } }, + }) + ).toResolveTruthy(); + + await expect(db.m2.findFirst()).resolves.toEqual(expect.objectContaining({ value: 2 })); + }); + + it('deep nesting', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/deep-nesting`, + ` + ${MODEL_PRELUDE} + + model M1 { + id String @id @default(uuid()) + m2 M2? + @@allow('all', true) + } + + model M2 { + id String @id @default(uuid()) + value Int + m1 M1 @relation(fields: [m1Id], references:[id]) + m1Id String @unique + m3 M3[] + + @@allow('create,read', true) + @@allow('update', future().value > 1) + } + + model M3 { + id String @id @default(uuid()) + value Int + m2 M2 @relation(fields: [m2Id], references:[id]) + m2Id String + + @@allow('create,read', true) + @@allow('update', future().value > 2) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.m1.create({ + data: { + id: '1', + m2: { + create: { + id: '1', + value: 0, + m3: { + create: [ + { id: '1', value: 0 }, + { id: '2', value: 1 }, + ], + }, + }, + }, + }, + }) + ).toResolveTruthy(); + + // rejected because nested m3 update fails post-update rule + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { update: { value: 2, m3: { updateMany: { where: {}, data: { value: { increment: 2 } } } } } }, + }, + }) + ).toBeRejectedByPolicy(); + + // rejected because nested m2 update fails post-update rule + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { update: { value: 1, m3: { updateMany: { where: {}, data: { value: { increment: 3 } } } } } }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m1.update({ + where: { id: '1' }, + data: { + m2: { update: { value: 2, m3: { updateMany: { where: {}, data: { value: { increment: 3 } } } } } }, + }, + }) + ).toResolveTruthy(); + + await expect(db.m2.findFirst()).resolves.toEqual(expect.objectContaining({ value: 2 })); + await expect(db.m3.findMany()).resolves.toEqual( + expect.arrayContaining([expect.objectContaining({ value: 3 }), expect.objectContaining({ value: 4 })]) + ); + }); +}); diff --git a/tests/integration/tests/with-policy/todo-sample.test.ts b/tests/integration/tests/with-policy/todo-sample.test.ts new file mode 100644 index 000000000..7ae4e3233 --- /dev/null +++ b/tests/integration/tests/with-policy/todo-sample.test.ts @@ -0,0 +1,510 @@ +import { AuthUser } from '@zenstackhq/runtime'; +import path from 'path'; +import { WeakDbClientContract, loadPrismaFromModelFile, run } from '../../utils'; + +describe('Todo Policy Tests', () => { + let getDb: (user?: AuthUser) => WeakDbClientContract; + let prisma: WeakDbClientContract; + + beforeAll(async () => { + const { withPolicy, prisma: _prisma } = await loadPrismaFromModelFile( + 'todo-policy', + path.join(__dirname, '../schema/todo.zmodel') + ); + getDb = withPolicy; + prisma = _prisma; + }); + + beforeEach(() => { + run('npx prisma migrate reset --force'); + run('npx prisma db push'); + }); + + it('user', async () => { + const user1 = { + id: 'user1', + email: 'user1@zenstack.dev', + name: 'User 1', + }; + const user2 = { + id: 'user2', + email: 'user2@zenstack.dev', + name: 'User 2', + }; + + const anonDb = getDb(); + const user1Db = getDb({ id: user1.id }); + const user2Db = getDb({ id: user2.id }); + + // create user1 + // create should succeed but result can be read back anonymously + await expect(anonDb.user.create({ data: user1 })).toBeRejectedByPolicy(); + await expect(user1Db.user.findUnique({ where: { id: user1.id } })).toResolveTruthy(); + await expect(user2Db.user.findUnique({ where: { id: user1.id } })).toResolveNull(); + + // create user2 + await expect(anonDb.user.create({ data: user2 })).toBeRejectedByPolicy(); + + // find with user1 should only get user1 + const r = await user1Db.user.findMany(); + expect(r).toHaveLength(1); + expect(r[0]).toEqual(expect.objectContaining(user1)); + + // get user2 as user1 + await expect(user1Db.user.findUnique({ where: { id: user2.id } })).toResolveNull(); + + // add both users into the same space + await expect( + user1Db.space.create({ + data: { + name: 'Space 1', + slug: 'space1', + owner: { connect: { id: user1.id } }, + members: { + create: [ + { + user: { connect: { id: user1.id } }, + role: 'ADMIN', + }, + { + user: { connect: { id: user2.id } }, + role: 'USER', + }, + ], + }, + }, + }) + ).toResolveTruthy(); + + // now both user1 and user2 should be visible + await expect(user1Db.user.findMany()).resolves.toHaveLength(2); + await expect(user2Db.user.findMany()).resolves.toHaveLength(2); + + // update user2 as user1 + await expect( + user2Db.user.update({ + where: { id: user1.id }, + data: { name: 'hello' }, + }) + ).toBeRejectedByPolicy(); + + // update user1 as user1 + await expect( + user1Db.user.update({ + where: { id: user1.id }, + data: { name: 'hello' }, + }) + ).toResolveTruthy(); + + // delete user2 as user1 + await expect(user1Db.user.delete({ where: { id: user2.id } })).toBeRejectedByPolicy(); + + // delete user1 as user1 + await expect(user1Db.user.delete({ where: { id: user1.id } })).toResolveTruthy(); + await expect(user1Db.user.findUnique({ where: { id: user1.id } })).toResolveNull(); + }); + + it('todo list', async () => { + await createSpaceAndUsers(prisma); + + const anonDb = getDb(); + const emptyUIDDb = getDb({ id: '' }); + const user1Db = getDb({ id: user1.id }); + const user2Db = getDb({ id: user2.id }); + const user3Db = getDb({ id: user3.id }); + + await expect( + anonDb.list.create({ + data: { + id: 'list1', + title: 'List 1', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + user1Db.list.create({ + data: { + id: 'list1', + title: 'List 1', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }) + ).toResolveTruthy(); + + await expect(user1Db.list.findMany()).resolves.toHaveLength(1); + await expect(anonDb.list.findMany()).resolves.toHaveLength(0); + await expect(emptyUIDDb.list.findMany()).resolves.toHaveLength(0); + await expect(anonDb.list.findUnique({ where: { id: 'list1' } })).toResolveNull(); + + // accessible to owner + await expect(user1Db.list.findUnique({ where: { id: 'list1' } })).resolves.toEqual( + expect.objectContaining({ id: 'list1', title: 'List 1' }) + ); + + // accessible to user in the space + await expect(user2Db.list.findUnique({ where: { id: 'list1' } })).toResolveTruthy(); + + // inaccessible to user not in the space + await expect(user3Db.list.findUnique({ where: { id: 'list1' } })).toResolveNull(); + + // make a private list + await user1Db.list.create({ + data: { + id: 'list2', + title: 'List 2', + private: true, + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }); + + // accessible to owner + await expect(user1Db.list.findUnique({ where: { id: 'list2' } })).toResolveTruthy(); + + // inaccessible to other user in the space + await expect(user2Db.list.findUnique({ where: { id: 'list2' } })).toResolveNull(); + + // create a list which doesn't match credential should fail + await expect( + user1Db.list.create({ + data: { + id: 'list3', + title: 'List 3', + owner: { connect: { id: user2.id } }, + space: { connect: { id: space1.id } }, + }, + }) + ).toBeRejectedByPolicy(); + + // create a list which doesn't match credential's space should fail + await expect( + user1Db.list.create({ + data: { + id: 'list3', + title: 'List 3', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space2.id } }, + }, + }) + ).toBeRejectedByPolicy(); + + // update list + await expect( + user1Db.list.update({ + where: { id: 'list1' }, + data: { + title: 'List 1 updated', + }, + }) + ).resolves.toEqual(expect.objectContaining({ title: 'List 1 updated' })); + + await expect( + user2Db.list.update({ + where: { id: 'list1' }, + data: { + title: 'List 1 updated', + }, + }) + ).toBeRejectedByPolicy(); + + // delete list + await expect(user2Db.list.delete({ where: { id: 'list1' } })).toBeRejectedByPolicy(); + await expect(user1Db.list.delete({ where: { id: 'list1' } })).toResolveTruthy(); + await expect(user1Db.list.findUnique({ where: { id: 'list1' } })).toResolveNull(); + }); + + it('todo', async () => { + await createSpaceAndUsers(prisma); + + const user1Db = getDb({ id: user1.id }); + const user2Db = getDb({ id: user2.id }); + + // create a public list + await user1Db.list.create({ + data: { + id: 'list1', + title: 'List 1', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }); + + // create + await expect( + user1Db.todo.create({ + data: { + id: 'todo1', + title: 'Todo 1', + owner: { connect: { id: user1.id } }, + list: { + connect: { id: 'list1' }, + }, + }, + }) + ).toResolveTruthy(); + + await expect( + user2Db.todo.create({ + data: { + id: 'todo2', + title: 'Todo 2', + owner: { connect: { id: user2.id } }, + list: { + connect: { id: 'list1' }, + }, + }, + }) + ).toResolveTruthy(); + + // read + await expect(user1Db.todo.findMany()).resolves.toHaveLength(2); + await expect(user2Db.todo.findMany()).resolves.toHaveLength(2); + + // update, user in the same space can freely update + await expect( + user1Db.todo.update({ + where: { id: 'todo1' }, + data: { + title: 'Todo 1 updated', + }, + }) + ).toResolveTruthy(); + await expect( + user1Db.todo.update({ + where: { id: 'todo2' }, + data: { + title: 'Todo 2 updated', + }, + }) + ).toResolveTruthy(); + + // create a private list + await user1Db.list.create({ + data: { + id: 'list2', + private: true, + title: 'List 2', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }); + + // create + await expect( + user1Db.todo.create({ + data: { + id: 'todo3', + title: 'Todo 3', + owner: { connect: { id: user1.id } }, + list: { + connect: { id: 'list2' }, + }, + }, + }) + ).toResolveTruthy(); + + // reject because list2 is private + await expect( + user2Db.todo.create({ + data: { + id: 'todo4', + title: 'Todo 4', + owner: { connect: { id: user2.id } }, + list: { + connect: { id: 'list2' }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + // update, only owner can update todo in a private list + await expect( + user1Db.todo.update({ + where: { id: 'todo3' }, + data: { + title: 'Todo 3 updated', + }, + }) + ).toResolveTruthy(); + await expect( + user2Db.todo.update({ + where: { id: 'todo3' }, + data: { + title: 'Todo 3 updated', + }, + }) + ).toBeRejectedByPolicy(); + }); + + it('relation query', async () => { + await createSpaceAndUsers(prisma); + + const user1Db = getDb({ id: user1.id }); + const user2Db = getDb({ id: user2.id }); + + await user1Db.list.create({ + data: { + id: 'list1', + title: 'List 1', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }); + + await user1Db.list.create({ + data: { + id: 'list2', + title: 'List 2', + private: true, + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + }, + }); + + const r = await user1Db.space.findFirst({ + where: { id: 'space1' }, + include: { lists: true }, + }); + expect(r.lists).toHaveLength(2); + + const r1 = await user2Db.space.findFirst({ + where: { id: 'space1' }, + include: { lists: true }, + }); + expect(r1.lists).toHaveLength(1); + }); + + it('post-update checks', async () => { + await createSpaceAndUsers(prisma); + + const user1Db = getDb({ id: user1.id }); + + await user1Db.list.create({ + data: { + id: 'list1', + title: 'List 1', + owner: { connect: { id: user1.id } }, + space: { connect: { id: space1.id } }, + todos: { + create: { + id: 'todo1', + title: 'Todo 1', + owner: { connect: { id: user1.id } }, + }, + }, + }, + }); + + // change list's owner + await expect( + user1Db.list.update({ + where: { id: 'list1' }, + data: { + owner: { connect: { id: user2.id } }, + }, + }) + ).toBeRejectedByPolicy(); + + // change todo's owner + await expect( + user1Db.todo.update({ + where: { id: 'todo1' }, + data: { + owner: { connect: { id: user2.id } }, + }, + }) + ).toBeRejectedByPolicy(); + + // nested change todo's owner + await expect( + user1Db.list.update({ + where: { id: 'list1' }, + data: { + todos: { + update: { + where: { id: 'todo1' }, + data: { + owner: { connect: { id: user2.id } }, + }, + }, + }, + }, + }) + ).toBeRejectedByPolicy(); + }); +}); + +const user1 = { + id: 'user1', + email: 'user1@zenstack.dev', + name: 'User 1', +}; + +const user2 = { + id: 'user2', + email: 'user2@zenstack.dev', + name: 'User 2', +}; + +const user3 = { + id: 'user3', + email: 'user3@zenstack.dev', + name: 'User 3', +}; + +const space1 = { + id: 'space1', + name: 'Space 1', + slug: 'space1', +}; + +const space2 = { + id: 'space2', + name: 'Space 2', + slug: 'space2', +}; + +async function createSpaceAndUsers(db: WeakDbClientContract) { + // create users + await db.user.create({ data: user1 }); + await db.user.create({ data: user2 }); + await db.user.create({ data: user3 }); + + // add user1 and user2 into space1 + await db.space.create({ + data: { + ...space1, + members: { + create: [ + { + user: { connect: { id: user1.id } }, + role: 'ADMIN', + }, + { + user: { connect: { id: user2.id } }, + role: 'USER', + }, + ], + }, + }, + }); + + // add user3 to space2 + await db.space.create({ + data: { + ...space2, + members: { + create: [ + { + user: { connect: { id: user3.id } }, + role: 'ADMIN', + }, + ], + }, + }, + }); +} diff --git a/tests/integration/tests/with-policy/toplevel-operations.test.ts b/tests/integration/tests/with-policy/toplevel-operations.test.ts new file mode 100644 index 000000000..32f053b98 --- /dev/null +++ b/tests/integration/tests/with-policy/toplevel-operations.test.ts @@ -0,0 +1,208 @@ +import { MODEL_PRELUDE, loadPrisma } from '../../utils/utils'; +import path from 'path'; + +describe('With Policy:toplevel operations', () => { + let origDir: string; + const suite = 'toplevel'; + + beforeAll(async () => { + origDir = path.resolve('.'); + }); + + afterEach(async () => { + process.chdir(origDir); + }); + + it('read tests', async () => { + const { withPolicy, prisma } = await loadPrisma( + `${suite}/read`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('create', true) + @@allow('read', value > 1) + } + ` + ); + + const db = withPolicy(); + + await expect( + db.model.create({ + data: { + id: '1', + value: 1, + }, + }) + ).toBeRejectedByPolicy(); + const fromPrisma = await prisma.model.findUnique({ + where: { id: '1' }, + }); + expect(fromPrisma).toBeTruthy(); + + expect(await db.model.findMany()).toHaveLength(0); + expect(await db.model.findUnique({ where: { id: '1' } })).toBeNull(); + expect(await db.model.findFirst({ where: { id: '1' } })).toBeNull(); + await expect(db.model.findUniqueOrThrow({ where: { id: '1' } })).toBeNotFound(); + await expect(db.model.findFirstOrThrow({ where: { id: '1' } })).toBeNotFound(); + + const item2 = { + id: '2', + value: 2, + }; + const r1 = await db.model.create({ + data: item2, + }); + expect(r1).toBeTruthy(); + expect(await db.model.findMany()).toHaveLength(1); + expect(await db.model.findUnique({ where: { id: '2' } })).toEqual(expect.objectContaining(item2)); + expect(await db.model.findFirst({ where: { id: '2' } })).toEqual(expect.objectContaining(item2)); + expect(await db.model.findUniqueOrThrow({ where: { id: '2' } })).toEqual(expect.objectContaining(item2)); + expect(await db.model.findFirstOrThrow({ where: { id: '2' } })).toEqual(expect.objectContaining(item2)); + }); + + it('write tests', async () => { + const { withPolicy } = await loadPrisma( + `${suite}/write`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('read', value > 1) + @@allow('create', value > 0) + @@allow('update', value > 1) + } + ` + ); + + const db = withPolicy(); + + // create denied + await expect( + db.model.create({ + data: { + value: 0, + }, + }) + ).toBeRejectedByPolicy(); + + // can't read back + await expect( + db.model.create({ + data: { + id: '1', + value: 1, + }, + }) + ).toBeRejectedByPolicy(); + + // success + expect( + await db.model.create({ + data: { + id: '2', + value: 2, + }, + }) + ).toBeTruthy(); + + // update not found + await expect(db.model.update({ where: { id: '3' }, data: { value: 5 } })).toBeNotFound(); + + // update-many empty + expect( + await db.model.updateMany({ + where: { id: '3' }, + data: { value: 5 }, + }) + ).toEqual(expect.objectContaining({ count: 0 })); + + // upsert + expect( + await db.model.upsert({ + where: { id: '3' }, + create: { id: '3', value: 5 }, + update: { value: 6 }, + }) + ).toEqual(expect.objectContaining({ value: 5 })); + + // update denied + await expect( + db.model.update({ + where: { id: '1' }, + data: { + value: 3, + }, + }) + ).toBeRejectedByPolicy(); + + // update success + expect( + await db.model.update({ + where: { id: '2' }, + data: { + value: 3, + }, + }) + ).toBeTruthy(); + }); + + it('delete tests', async () => { + const { withPolicy, prisma } = await loadPrisma( + `${suite}/delete`, + ` + ${MODEL_PRELUDE} + + model Model { + id String @id @default(uuid()) + value Int + + @@allow('create', true) + @@allow('read', value > 2) + @@allow('delete', value > 1) + } + ` + ); + + const db = withPolicy(); + + await expect(db.model.delete({ where: { id: '1' } })).toBeNotFound(); + + await expect( + db.model.create({ + data: { id: '1', value: 1 }, + }) + ).toBeRejectedByPolicy(); + + await expect(db.model.delete({ where: { id: '1' } })).toBeRejectedByPolicy(); + expect(await prisma.model.findUnique({ where: { id: '1' } })).toBeTruthy(); + + await expect( + db.model.create({ + data: { id: '2', value: 2 }, + }) + ).toBeRejectedByPolicy(); + // deleted but unable to read back + await expect(db.model.delete({ where: { id: '2' } })).toBeRejectedByPolicy(); + expect(await prisma.model.findUnique({ where: { id: '2' } })).toBeNull(); + + await expect( + db.model.create({ + data: { id: '2', value: 2 }, + }) + ).toBeRejectedByPolicy(); + // only '2' is deleted, '1' is rejected by policy + expect(await db.model.deleteMany()).toEqual(expect.objectContaining({ count: 1 })); + expect(await prisma.model.findUnique({ where: { id: '2' } })).toBeNull(); + expect(await prisma.model.findUnique({ where: { id: '1' } })).toBeTruthy(); + + expect(await db.model.deleteMany()).toEqual(expect.objectContaining({ count: 0 })); + }); +}); diff --git a/tests/integration/tsconfig.json b/tests/integration/tsconfig.json index 18a6bedec..98869ecf1 100644 --- a/tests/integration/tsconfig.json +++ b/tests/integration/tsconfig.json @@ -6,5 +6,6 @@ "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true - } + }, + "include": ["**/*.ts", "**/*.d.ts"] } diff --git a/tests/integration/utils/index.ts b/tests/integration/utils/index.ts new file mode 100644 index 000000000..04bca77e0 --- /dev/null +++ b/tests/integration/utils/index.ts @@ -0,0 +1 @@ +export * from './utils'; diff --git a/tests/integration/utils/jest-ext.ts b/tests/integration/utils/jest-ext.ts new file mode 100644 index 000000000..e342bf7bd --- /dev/null +++ b/tests/integration/utils/jest-ext.ts @@ -0,0 +1,147 @@ +import { PrismaClientKnownRequestError } from '@prisma/client/runtime'; +import { format } from 'util'; + +export const toBeRejectedByPolicy = async function (received: Promise, expectedMessages?: string[]) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + await received; + } catch (err: any) { + if (expectedMessages) { + const message = err.message || ''; + for (const m of expectedMessages) { + if (!message.includes(m)) { + return { + message: () => `expected message not found in error: ${m}, got message: ${message}`, + pass: false, + }; + } + } + } + return expectPrismaCode(err, 'P2004'); + } + return { + message: () => `expected PrismaClientKnownRequestError, got no error`, + pass: false, + }; +}; + +export const toBeNotFound = async function (received: Promise) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + await received; + } catch (err) { + return expectPrismaCode(err, 'P2025'); + } + return { + message: () => `expected PrismaClientKnownRequestError, got no error`, + pass: false, + }; +}; + +export const toBeRejectedWithCode = async function (received: Promise, code: string) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + await received; + } catch (err) { + return expectPrismaCode(err, code); + } + return { + message: () => `expected PrismaClientKnownRequestError, got no error`, + pass: false, + }; +}; + +export const toResolveTruthy = async function (received: Promise) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + const r = await received; + if (r) { + return { + message: () => '', + pass: true, + }; + } else { + return { + message: () => 'resolved to a falsy value', + pass: false, + }; + } + } catch (err) { + return { + message: () => `promise rejected: ${err}`, + pass: false, + }; + } +}; + +export const toResolveFalsy = async function (received: Promise) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + const r = await received; + if (!r) { + return { + message: () => '', + pass: true, + }; + } else { + return { + message: () => `resolved to a truthy value: ${r}`, + pass: false, + }; + } + } catch (err) { + return { + message: () => `promise rejected: ${err}`, + pass: false, + }; + } +}; + +export const toResolveNull = async function (received: Promise) { + if (!(received instanceof Promise)) { + return { message: () => 'a promise is expected', pass: false }; + } + try { + const r = await received; + if (r === null) { + return { + message: () => '', + pass: true, + }; + } else { + return { + message: () => `resolved to a non-null value: ${format(r)}`, + pass: false, + }; + } + } catch (err) { + return { + message: () => `promise rejected: ${err}`, + pass: false, + }; + } +}; + +function expectPrismaCode(err: any, code: string) { + const errCode = (err as PrismaClientKnownRequestError).code; + if (errCode !== code) { + return { + message: () => `expected PrismaClientKnownRequestError.code 'P2004', got ${errCode ?? err}`, + pass: false, + }; + } + return { + message: () => '', + pass: true, + }; +} diff --git a/tests/integration/utils/utils.ts b/tests/integration/utils/utils.ts new file mode 100644 index 000000000..5751f9022 --- /dev/null +++ b/tests/integration/utils/utils.ts @@ -0,0 +1,82 @@ +import { AuthUser, DbOperations, withOmit, withPassword, withPolicy, withPresets } from '@zenstackhq/runtime'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; + +export const MODEL_PRELUDE = ` +datasource db { + provider = 'sqlite' + url = 'file:./operations.db' +} + +generator js { + provider = 'prisma-client-js' + output = '../.prisma' + previewFeatures = ['clientExtensions'] +} + +plugin meta { + provider = '@zenstack/model-meta' + output = '.zenstack' +} + +plugin policy { + provider = '@zenstack/access-policy' + output = '.zenstack' +} +`; + +export function run(cmd: string, env?: Record, cwd?: string) { + execSync(cmd, { + stdio: 'pipe', + encoding: 'utf-8', + env: { ...process.env, DO_NOT_TRACK: '1', ...env }, + cwd, + }); +} + +export type WeakDbClientContract = Record & { + $on(eventType: any, callback: (event: any) => void): void; + $use(cb: any): void; + $disconnect: () => Promise; + $transaction: (input: ((tx: WeakDbClientContract) => Promise) | any[], options?: any) => Promise; + $queryRaw: (query: TemplateStringsArray, ...args: any[]) => Promise; + $executeRaw: (query: TemplateStringsArray, ...args: any[]) => Promise; + $extends: (args: any) => WeakDbClientContract; +}; + +export type WeakDbOperations = { + [key in keyof DbOperations]: (...args: any[]) => Promise; +}; + +export async function loadPrismaFromModelFile(testName: string, modelFile: string) { + const content = fs.readFileSync(modelFile, { encoding: 'utf-8' }); + return loadPrisma(testName, content); +} + +export async function loadPrisma(testName: string, model: string) { + const workDir = path.resolve('test-run/cases', testName); + if (fs.existsSync(workDir)) { + fs.rmSync(workDir, { recursive: true, force: true }); + } + fs.mkdirSync(workDir, { recursive: true }); + process.chdir(workDir); + + fs.writeFileSync('schema.zmodel', model); + run('npx zenstack generate'); + run('npx prisma db push'); + + const PrismaClient = require(path.join(workDir, '.prisma')).PrismaClient; + const prisma = new PrismaClient({ log: ['info', 'warn', 'error'] }); + + const policy = require(path.join(workDir, '.zenstack/policy')).default; + const modelMeta = require(path.join(workDir, '.zenstack/model-meta')).default; + + return { + prisma, + withPolicy: (user?: AuthUser) => withPolicy(prisma, { user }, policy, modelMeta), + withOmit: () => withOmit(prisma, modelMeta), + withPassword: () => withPassword(prisma, modelMeta), + withPresets: (user?: AuthUser) => withPresets(prisma, { user }, policy, modelMeta), + }; +}