From e2d62fc4217ca448bbf38999d3eb7359d303b8f7 Mon Sep 17 00:00:00 2001 From: Yiming <104139426+ymc9@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:31:44 +0800 Subject: [PATCH 1/2] merge canary to dev (#181) Co-authored-by: JS --- .changeset/config.json | 18 +- .github/ISSUE_TEMPLATE/bug_report.md | 17 +- .github/ISSUE_TEMPLATE/feature_request.md | 1 - .github/workflows/build-test.yml | 13 +- .gitignore | 1 + .prettierignore | 17 + .prettierrc | 6 + .vscode/extensions.json | 2 +- .vscode/launch.json | 17 +- CODE_OF_CONDUCT.md | 32 +- README.md | 88 +- SECURITY.md | 2 +- doc-serve/.eslintrc.json | 3 - doc-serve/middleware.ts | 31 - doc-serve/package-lock.json | 5107 ---------- doc-serve/package.json | 22 - .../8cd31ff7afe74424abbec00b65f04ae8.txt | 1 - doc-serve/public/README.md | 28 - doc-serve/public/_coverpage.md | 12 - doc-serve/public/_media/banner.png | Bin 33040 -> 0 bytes doc-serve/public/_media/cli-shot.png | Bin 156029 -> 0 bytes doc-serve/public/_media/logo.png | Bin 23566 -> 0 bytes doc-serve/public/_media/starter-shot.png | Bin 89412 -> 0 bytes doc-serve/public/_navbar.md | 1 - doc-serve/public/_sidebar.md | 38 - doc-serve/public/building-your-app.md | 149 - doc-serve/public/choosing-a-database.md | 11 - doc-serve/public/cli-commands.md | 131 - doc-serve/public/code-generation.md | 15 - doc-serve/public/entity-types-server.md | 84 - doc-serve/public/entity-types.md | 84 - .../public/evolving-model-with-migration.md | 65 - doc-serve/public/index.html | 99 - .../public/integrating-authentication.md | 228 - doc-serve/public/modeling-your-app.md | 168 - doc-serve/public/quick-start.md | 134 - doc-serve/public/reach-out.md | 9 - doc-serve/public/robots.txt | 2 - doc-serve/public/runtime-api.md | 231 - doc-serve/public/server-side-rendering.md | 26 - doc-serve/public/setup-logging.md | 92 - doc-serve/public/sitemap.xml | 1 - .../public/static/building-your-app.html | 231 - doc-serve/public/static/changelog.html | 158 - .../public/static/choosing-a-database.html | 158 - doc-serve/public/static/cli-commands.html | 164 - doc-serve/public/static/code-generation.html | 158 - .../public/static/entity-types-server.html | 205 - doc-serve/public/static/entity-types.html | 205 - .../static/evolving-model-with-migration.html | 158 - doc-serve/public/static/index.html | 163 - .../static/integrating-authentication.html | 303 - .../public/static/modeling-your-app.html | 247 - doc-serve/public/static/quick-start.html | 159 - doc-serve/public/static/reach-out.html | 159 - doc-serve/public/static/runtime-api.html | 252 - .../public/static/server-side-rendering.html | 173 - doc-serve/public/static/setup-logging.html | 193 - doc-serve/public/static/telemetry.html | 158 - doc-serve/public/static/vscode-extension.html | 158 - .../public/static/zmodel-access-policy.html | 264 - doc-serve/public/static/zmodel-attribute.html | 391 - .../public/static/zmodel-data-model.html | 162 - .../public/static/zmodel-data-source.html | 251 - doc-serve/public/static/zmodel-enum.html | 163 - .../static/zmodel-field-constraint.html | 164 - doc-serve/public/static/zmodel-field.html | 182 - doc-serve/public/static/zmodel-overview.html | 158 - .../static/zmodel-referential-action.html | 179 - doc-serve/public/static/zmodel-relation.html | 203 - doc-serve/public/telemetry.md | 21 - doc-serve/public/vscode-extension.md | 5 - doc-serve/public/zmodel-access-policy.md | 203 - doc-serve/public/zmodel-attribute.md | 480 - doc-serve/public/zmodel-data-model.md | 35 - doc-serve/public/zmodel-data-source.md | 80 - doc-serve/public/zmodel-enum.md | 30 - doc-serve/public/zmodel-field-constraint.md | 71 - doc-serve/public/zmodel-field.md | 66 - doc-serve/public/zmodel-overview.md | 13 - doc-serve/public/zmodel-referential-action.md | 68 - doc-serve/public/zmodel-relation.md | 88 - doc-serve/vercel.json | 3 - docs/building-your-app.md | 5 +- docs/index.html | 42 +- docs/runtime-api.md | 11 +- docs/script/sitemap.js | 14 +- docs/script/ssg.js | 14 +- docs/sitemap.js | 20 + docs/vercel.json | 13 + package.json | 7 +- packages/{runtime/LICENSE.md => LICENSE} | 0 packages/README.md | 122 + packages/language/.eslintrc.json | 15 + packages/language/LICENSE | 1 + packages/language/README.md | 1 + .../{schema => language}/langium-config.json | 4 +- packages/language/package.json | 32 + packages/language/src/ast.ts | 53 + .../src}/generated/ast.ts | 233 +- .../src}/generated/grammar.ts | 518 +- .../src}/generated/module.ts | 2 +- packages/language/src/module.ts | 1 + .../src}/zmodel.langium | 38 +- .../language/syntaxes/zmodel.tmLanguage.json | 57 + .../{next-auth => language}/tsconfig.json | 0 packages/next-auth/README.md | 5 - packages/next-auth/package.json | 46 - packages/next-auth/src/adapter.ts | 11 - packages/next-auth/src/authorize.ts | 59 - packages/next-auth/src/index.ts | 2 - packages/{next-auth => next}/.eslintrc.json | 0 packages/next/LICENSE | 1 + packages/next/README.md | 5 + packages/next/package.json | 37 + packages/next/src/index.ts | 1 + packages/next/src/request-handler.ts | 168 + packages/next/tsconfig.json | 22 + packages/plugins/react/.eslintrc.json | 14 + packages/plugins/react/LICENSE | 1 + packages/plugins/react/README.md | 3 + packages/plugins/react/package.json | 41 + packages/plugins/react/src/index.ts | 10 + .../react/src/react-hooks-generator.ts | 500 + .../react/src/runtime.ts} | 81 +- .../plugins/react/src/serialization-utils.ts | 21 + .../react/tsconfig.json} | 12 +- packages/plugins/trpc/.eslintrc.json | 14 + packages/plugins/trpc/LICENSE | 1 + packages/plugins/trpc/README.md | 3 + packages/plugins/trpc/package.json | 42 + packages/plugins/trpc/src/config.ts | 7 + packages/plugins/trpc/src/generator.ts | 164 + packages/plugins/trpc/src/helpers.ts | 148 + packages/plugins/trpc/src/index.ts | 10 + packages/plugins/trpc/src/project.ts | 15 + .../plugins/trpc/src/types.ts | 0 packages/plugins/trpc/src/utils/removeDir.ts | 15 + .../trpc/src/utils/uncapitalizeFirstLetter.ts | 3 + packages/plugins/trpc/src/zod/README.md | 1 + packages/plugins/trpc/src/zod/generator.ts | 101 + .../trpc/src/zod/helpers/aggregate-helpers.ts | 78 + .../trpc/src/zod/helpers/comments-helpers.ts | 112 + .../plugins/trpc/src/zod/helpers/helpers.ts | 52 + .../trpc/src/zod/helpers/include-helpers.ts | 88 + .../plugins/trpc/src/zod/helpers/index.ts | 4 + .../trpc/src/zod/helpers/model-helpers.ts | 36 + .../trpc/src/zod/helpers/modelArgs-helpers.ts | 73 + .../trpc/src/zod/helpers/mongodb-helpers.ts | 73 + .../trpc/src/zod/helpers/select-helpers.ts | 155 + packages/plugins/trpc/src/zod/index.ts | 10 + packages/plugins/trpc/src/zod/transformer.ts | 640 ++ packages/plugins/trpc/src/zod/types.ts | 22 + .../plugins/trpc/src/zod/utils/formatFile.ts | 31 + .../plugins/trpc/src/zod/utils/removeDir.ts | 15 + .../trpc/src/zod/utils/writeFileSafely.ts | 11 + packages/plugins/trpc/tsconfig.json | 21 + packages/runtime/LICENSE | 1 + packages/runtime/package.json | 8 +- packages/runtime/pre/client/index.d.ts | 4 - packages/runtime/pre/client/index.js | 12 - packages/runtime/pre/server/auth.d.ts | 1 - packages/runtime/pre/server/auth.js | 3 - packages/runtime/pre/server/index.d.ts | 18 - packages/runtime/pre/server/index.js | 7 - packages/runtime/pre/types/index.d.ts | 1 - packages/runtime/pre/types/index.js | 3 - packages/runtime/src/config.ts | 16 - packages/runtime/src/constants.ts | 10 - packages/runtime/src/enhancements/index.ts | 4 + .../runtime/src/enhancements/model-meta.ts | 21 + .../src/enhancements/nested-write-vistor.ts | 221 + packages/runtime/src/enhancements/omit.ts | 53 + packages/runtime/src/enhancements/password.ts | 60 + .../src/enhancements/policy/handler.ts | 298 + .../runtime/src/enhancements/policy/index.ts | 48 + .../runtime/src/enhancements/policy/logger.ts | 44 + .../src/enhancements/policy/policy-utils.ts | 642 ++ packages/runtime/src/enhancements/preset.ts | 26 + packages/runtime/src/enhancements/proxy.ts | 224 + packages/runtime/src/enhancements/types.ts | 31 + packages/runtime/src/enhancements/utils.ts | 31 + packages/runtime/src/error.ts | 12 + packages/runtime/src/handler/data/crud.ts | 457 - packages/runtime/src/handler/data/handler.ts | 200 - .../src/handler/data/nested-write-vistor.ts | 99 - .../runtime/src/handler/data/policy-utils.ts | 647 -- packages/runtime/src/handler/index.ts | 1 - packages/runtime/src/handler/types.ts | 54 - packages/runtime/src/index.ts | 6 +- packages/runtime/src/request-handler.ts | 47 - packages/runtime/src/service.ts | 201 - packages/runtime/src/types.ts | 220 +- packages/runtime/src/version.ts | 8 + packages/runtime/tsconfig.json | 2 +- packages/schema/.env | 1 - packages/schema/.eslintrc.json | 6 +- packages/schema/.gitignore | 2 +- packages/schema/LICENSE | 1 + packages/schema/LICENSE.md | 21 - packages/schema/README-global.md | 1 + packages/schema/README.md | 30 + packages/schema/bin/cli | 2 +- packages/schema/bin/post-install.js | 24 + packages/schema/build/bundle.js | 37 +- packages/schema/build/env-plugin.js | 12 +- packages/schema/build/post-build.js | 29 + packages/schema/jest.config.ts | 5 - packages/schema/language-configuration.json | 4 +- packages/schema/package.json | 57 +- packages/schema/script/post-install.js | 24 - packages/schema/src/cli/cli-util.ts | 176 +- packages/schema/src/cli/index.ts | 296 +- packages/schema/src/cli/plugin-runner.ts | 160 + packages/schema/src/constants.ts | 2 + packages/schema/src/extension.ts | 25 +- packages/schema/src/generator/ast-utils.ts | 18 - packages/schema/src/generator/constants.ts | 6 - .../src/generator/field-constraint/index.ts | 304 - packages/schema/src/generator/index.ts | 86 - packages/schema/src/generator/prisma/index.ts | 44 - .../generator/prisma/query-guard-generator.ts | 249 - .../schema/src/generator/react-hooks/index.ts | 273 - .../schema/src/generator/service/index.ts | 113 - packages/schema/src/generator/tsc/index.ts | 59 - .../schema/src/language-server/constants.ts | 19 +- .../src/language-server/langium-ext.d.ts | 22 - packages/schema/src/language-server/types.ts | 14 - packages/schema/src/language-server/utils.ts | 4 +- .../validator/attribute-validator.ts | 4 +- .../validator/datamodel-validator.ts | 212 +- .../validator/datasource-validator.ts | 39 +- .../validator/enum-validator.ts | 4 +- .../validator/expression-validator.ts | 32 +- .../validator/schema-validator.ts | 12 +- .../src/language-server/validator/utils.ts | 48 +- .../validator/zmodel-validator.ts | 32 +- .../src/language-server/zmodel-formatter.ts | 20 + .../src/language-server/zmodel-linker.ts | 282 +- .../src/language-server/zmodel-module.ts | 55 +- .../src/language-server/zmodel-scope.ts | 13 +- .../zmodel-workspace-manager.ts | 4 +- .../access-policy}/expression-writer.ts | 275 +- .../schema/src/plugins/access-policy/index.ts | 9 + .../access-policy/policy-guard-generator.ts | 384 + .../typescript-expression-transformer.ts | 47 +- .../schema/src/plugins/access-policy/utils.ts | 9 + .../access-policy/zod-schema-generator.ts | 164 + .../schema/src/plugins/model-meta/index.ts | 121 + packages/schema/src/plugins/plugin-utils.ts | 48 + .../prisma}/indent-string.ts | 0 packages/schema/src/plugins/prisma/index.ts | 9 + .../prisma/prisma-builder.ts | 190 +- .../prisma/schema-generator.ts | 216 +- .../plugins/prisma/zmodel-code-generator.ts | 160 + packages/schema/src/res/package.template.json | 9 - packages/schema/src/res/prism-zmodel.js | 6 +- packages/schema/src/res/starter.zmodel | 47 + packages/schema/src/res/stdlib.zmodel | 13 +- packages/schema/src/telemetry.ts | 36 +- packages/schema/src/{generator => }/types.ts | 10 +- packages/schema/src/utils/ast-utils.ts | 105 + packages/schema/src/utils/exec-utils.ts | 7 +- packages/schema/src/utils/pkg-utils.ts | 19 +- packages/schema/src/utils/version-utils.ts | 8 + packages/schema/syntaxes/zmodel.json | 100 +- .../schema/syntaxes/zmodel.tmLanguage.json | 100 +- packages/schema/tests/cli/cli.test.ts | 77 + .../tests/generator/code-generator.test.ts | 105 + .../tests/generator/expression-writer.test.ts | 90 +- .../tests/generator/prisma-builder.test.ts | 73 +- .../tests/generator/prisma-generator.test.ts | 64 + .../schema/tests/schema/formatter.test.ts | 36 + packages/schema/tests/schema/parser.test.ts | 81 +- .../schema/tests/schema/sample-todo.test.ts | 12 +- packages/schema/tests/schema/stdlib.test.ts | 18 +- .../schema/tests/schema/todo.zmodel | 28 +- .../validation/attribute-validation.test.ts | 22 +- .../validation/datamodel-validation.test.ts | 115 +- .../validation/datasource-validation.test.ts | 16 +- .../validation/schema-validation.test.ts | 4 +- packages/schema/tests/utils.ts | 28 +- packages/schema/tsconfig.json | 10 +- packages/sdk/.eslintrc.json | 14 + packages/sdk/LICENSE | 1 + packages/sdk/README.md | 1 + packages/sdk/package.json | 30 + packages/sdk/src/ast.ts | 1 + packages/sdk/src/constants.ts | 14 + packages/sdk/src/index.ts | 3 + packages/sdk/src/types.ts | 30 + packages/sdk/src/utils.ts | 38 + packages/sdk/tsconfig.json | 22 + pnpm-lock.yaml | 2399 +++-- pnpm-workspace.yaml | 1 + samples/todo/.babelrc | 6 - samples/todo/.env | 4 - samples/todo/.eslintrc.json | 3 - samples/todo/.vscode/launch.json | 11 - samples/todo/README.md | 39 - samples/todo/components/AuthGuard.tsx | 24 - samples/todo/components/Avatar.tsx | 22 - samples/todo/components/BreadCrumb.tsx | 44 - samples/todo/components/ManageMembers.tsx | 137 - samples/todo/components/NavBar.tsx | 61 - samples/todo/components/SpaceMembers.tsx | 79 - samples/todo/components/Spaces.tsx | 29 - samples/todo/components/TimeInfo.tsx | 17 - samples/todo/components/Todo.tsx | 72 - samples/todo/components/TodoList.tsx | 77 - samples/todo/components/WithNavBar.tsx | 18 - samples/todo/lib/context.ts | 32 - samples/todo/lib/query-utils.ts | 11 - samples/todo/next.config.js | 14 - samples/todo/package-lock.json | 8720 ----------------- samples/todo/package.json | 51 - samples/todo/pages/_app.tsx | 48 - samples/todo/pages/api/auth/[...nextauth].ts | 88 - samples/todo/pages/api/zenstack/[...path].ts | 16 - samples/todo/pages/create-space.tsx | 125 - samples/todo/pages/index.tsx | 52 - samples/todo/pages/signin.tsx | 141 - samples/todo/pages/signup.tsx | 140 - .../pages/space/[slug]/[listId]/index.tsx | 137 - samples/todo/pages/space/[slug]/index.tsx | 234 - samples/todo/postcss.config.js | 6 - samples/todo/public/auth-bg.jpg | Bin 687040 -> 0 bytes samples/todo/public/avatar.jpg | Bin 29019 -> 0 bytes samples/todo/public/favicon.ico | Bin 25931 -> 0 bytes samples/todo/public/logo.png | Bin 11466 -> 0 bytes samples/todo/public/vercel.svg | 4 - samples/todo/styles/globals.css | 7 - samples/todo/tailwind.config.js | 11 - samples/todo/tsconfig.json | 28 - samples/todo/types/next-auth.d.ts | 14 - samples/todo/types/next.d.ts | 16 - samples/todo/zenstack.config.json | 3 - .../20221014084317_init/migration.sql | 153 - .../20221020094651_upate_cli/migration.sql | 23 - .../migration.sql | 41 - .../20221126150023_add_account/migration.sql | 28 - .../migration.sql | 3 - .../migration.sql | 2 - .../migration.sql | 8 - .../zenstack/migrations/migration_lock.toml | 3 - tests/integration/.eslintrc.json | 13 + tests/integration/.gitignore | 4 +- tests/integration/global-setup.js | 10 + tests/integration/jest.config.ts | 5 +- tests/integration/jest.d.ts | 16 + tests/integration/package.json | 19 +- tests/integration/test-run/package-lock.json | 459 + tests/integration/test-run/package.json | 22 + tests/integration/test-setup.ts | 17 + .../tests/e2e/prisma-methods.test.ts | 134 + .../tests/e2e/todo-presets.test.ts | 37 + .../tests/e2e/type-coverage.test.ts | 64 + .../tests/field-validation-client.test.ts | 208 - .../tests/field-validation-server.test.ts | 548 -- .../integration/tests/field-validation.zmodel | 44 - tests/integration/tests/logging.test.ts | 266 - .../tests/nextjs/generation.test.ts | 48 + .../tests/nextjs/test-project}/.gitignore | 0 .../tests/nextjs/test-project/.npmrc | 1 + .../tests/nextjs/test-project}/README.md | 0 .../tests/nextjs/test-project}/next.config.js | 0 .../nextjs/test-project/package-lock.json | 718 ++ .../tests/nextjs/test-project/package.json | 24 + .../tests/nextjs/test-project/pages/_app.tsx | 5 + .../test-project/pages/api/model/[...path].ts | 7 + .../tests/nextjs/test-project/pages/index.tsx | 13 + .../tests/nextjs/test-project/postgres.zmodel | 33 + .../tests/nextjs/test-project/server/db.ts | 12 + .../tests/nextjs/test-project/sqlite.zmodel | 33 + .../tests/nextjs/test-project}/tsconfig.json | 0 tests/integration/tests/omit.test.ts | 50 - tests/integration/tests/omit.zmodel | 21 - .../tests/operation-coverate.test.ts | 1123 --- tests/integration/tests/operations.zmodel | 150 - tests/integration/tests/password.test.ts | 79 - tests/integration/tests/password.zmodel | 20 - .../tests/{ => schema}/todo.zmodel | 27 +- tests/integration/tests/todo-e2e.test.ts | 592 -- .../integration/tests/trpc/generation.test.ts | 32 + .../tests/trpc/test-project}/.gitignore | 3 - .../tests/trpc/test-project/.npmrc | 1 + .../tests/trpc/test-project/README.md | 34 + .../tests/trpc/test-project/lib/trpc.ts | 32 + .../tests/trpc/test-project/next.config.js | 7 + .../tests/trpc/test-project/package-lock.json | 1020 ++ .../tests/trpc/test-project/package.json | 31 + .../tests/trpc/test-project/pages/_app.tsx | 8 + .../test-project/pages/api/model/[...path].ts | 7 + .../tests/trpc/test-project/pages/index.tsx | 12 + .../tests/trpc/test-project/server/context.ts | 12 + .../tests/trpc/test-project/server/db.ts | 12 + .../trpc/test-project/server/routers/_app.ts | 12 + .../tests/trpc/test-project/todo.zmodel | 33 + .../tests/trpc/test-project/tsconfig.json | 20 + tests/integration/tests/type-coverage.test.ts | 43 - tests/integration/tests/type-coverage.zmodel | 19 - tests/integration/tests/utils.ts | 174 - .../tests/with-omit/with-omit.test.ts | 82 + .../tests/with-password/with-password.test.ts | 48 + .../tests/with-policy/auth.test.ts | 213 + .../tests/with-policy/deep-nested.test.ts | 434 + .../tests/with-policy/empty-policy.test.ts | 126 + .../with-policy/field-validation.test.ts | 461 + .../tests/with-policy/nested-to-many.test.ts | 511 + .../tests/with-policy/nested-to-one.test.ts | 205 + .../tests/with-policy/post-update.test.ts | 351 + .../tests/with-policy/todo-sample.test.ts | 510 + .../with-policy/toplevel-operations.test.ts | 208 + tests/integration/tsconfig.json | 3 +- tests/integration/utils/index.ts | 1 + tests/integration/utils/jest-ext.ts | 147 + tests/integration/utils/utils.ts | 82 + 417 files changed, 16604 insertions(+), 33819 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 doc-serve/.eslintrc.json delete mode 100644 doc-serve/middleware.ts delete mode 100644 doc-serve/package-lock.json delete mode 100644 doc-serve/package.json delete mode 100644 doc-serve/public/8cd31ff7afe74424abbec00b65f04ae8.txt delete mode 100644 doc-serve/public/README.md delete mode 100644 doc-serve/public/_coverpage.md delete mode 100644 doc-serve/public/_media/banner.png delete mode 100644 doc-serve/public/_media/cli-shot.png delete mode 100644 doc-serve/public/_media/logo.png delete mode 100644 doc-serve/public/_media/starter-shot.png delete mode 100644 doc-serve/public/_navbar.md delete mode 100644 doc-serve/public/_sidebar.md delete mode 100644 doc-serve/public/building-your-app.md delete mode 100644 doc-serve/public/choosing-a-database.md delete mode 100644 doc-serve/public/cli-commands.md delete mode 100644 doc-serve/public/code-generation.md delete mode 100644 doc-serve/public/entity-types-server.md delete mode 100644 doc-serve/public/entity-types.md delete mode 100644 doc-serve/public/evolving-model-with-migration.md delete mode 100644 doc-serve/public/index.html delete mode 100644 doc-serve/public/integrating-authentication.md delete mode 100644 doc-serve/public/modeling-your-app.md delete mode 100644 doc-serve/public/quick-start.md delete mode 100644 doc-serve/public/reach-out.md delete mode 100644 doc-serve/public/robots.txt delete mode 100644 doc-serve/public/runtime-api.md delete mode 100644 doc-serve/public/server-side-rendering.md delete mode 100644 doc-serve/public/setup-logging.md delete mode 100644 doc-serve/public/sitemap.xml delete mode 100644 doc-serve/public/static/building-your-app.html delete mode 100644 doc-serve/public/static/changelog.html delete mode 100644 doc-serve/public/static/choosing-a-database.html delete mode 100644 doc-serve/public/static/cli-commands.html delete mode 100644 doc-serve/public/static/code-generation.html delete mode 100644 doc-serve/public/static/entity-types-server.html delete mode 100644 doc-serve/public/static/entity-types.html delete mode 100644 doc-serve/public/static/evolving-model-with-migration.html delete mode 100644 doc-serve/public/static/index.html delete mode 100644 doc-serve/public/static/integrating-authentication.html delete mode 100644 doc-serve/public/static/modeling-your-app.html delete mode 100644 doc-serve/public/static/quick-start.html delete mode 100644 doc-serve/public/static/reach-out.html delete mode 100644 doc-serve/public/static/runtime-api.html delete mode 100644 doc-serve/public/static/server-side-rendering.html delete mode 100644 doc-serve/public/static/setup-logging.html delete mode 100644 doc-serve/public/static/telemetry.html delete mode 100644 doc-serve/public/static/vscode-extension.html delete mode 100644 doc-serve/public/static/zmodel-access-policy.html delete mode 100644 doc-serve/public/static/zmodel-attribute.html delete mode 100644 doc-serve/public/static/zmodel-data-model.html delete mode 100644 doc-serve/public/static/zmodel-data-source.html delete mode 100644 doc-serve/public/static/zmodel-enum.html delete mode 100644 doc-serve/public/static/zmodel-field-constraint.html delete mode 100644 doc-serve/public/static/zmodel-field.html delete mode 100644 doc-serve/public/static/zmodel-overview.html delete mode 100644 doc-serve/public/static/zmodel-referential-action.html delete mode 100644 doc-serve/public/static/zmodel-relation.html delete mode 100644 doc-serve/public/telemetry.md delete mode 100644 doc-serve/public/vscode-extension.md delete mode 100644 doc-serve/public/zmodel-access-policy.md delete mode 100644 doc-serve/public/zmodel-attribute.md delete mode 100644 doc-serve/public/zmodel-data-model.md delete mode 100644 doc-serve/public/zmodel-data-source.md delete mode 100644 doc-serve/public/zmodel-enum.md delete mode 100644 doc-serve/public/zmodel-field-constraint.md delete mode 100644 doc-serve/public/zmodel-field.md delete mode 100644 doc-serve/public/zmodel-overview.md delete mode 100644 doc-serve/public/zmodel-referential-action.md delete mode 100644 doc-serve/public/zmodel-relation.md delete mode 100644 doc-serve/vercel.json create mode 100644 docs/sitemap.js create mode 100644 docs/vercel.json rename packages/{runtime/LICENSE.md => LICENSE} (100%) create mode 100644 packages/README.md create mode 100644 packages/language/.eslintrc.json create mode 120000 packages/language/LICENSE create mode 100644 packages/language/README.md rename packages/{schema => language}/langium-config.json (70%) create mode 100644 packages/language/package.json create mode 100644 packages/language/src/ast.ts rename packages/{schema/src/language-server => language/src}/generated/ast.ts (71%) rename packages/{schema/src/language-server => language/src}/generated/grammar.ts (83%) rename packages/{schema/src/language-server => language/src}/generated/module.ts (95%) create mode 100644 packages/language/src/module.ts rename packages/{schema/src/language-server => language/src}/zmodel.langium (82%) create mode 100644 packages/language/syntaxes/zmodel.tmLanguage.json rename packages/{next-auth => language}/tsconfig.json (100%) delete mode 100644 packages/next-auth/README.md delete mode 100644 packages/next-auth/package.json delete mode 100644 packages/next-auth/src/adapter.ts delete mode 100644 packages/next-auth/src/authorize.ts delete mode 100644 packages/next-auth/src/index.ts rename packages/{next-auth => next}/.eslintrc.json (100%) create mode 120000 packages/next/LICENSE create mode 100644 packages/next/README.md create mode 100644 packages/next/package.json create mode 100644 packages/next/src/index.ts create mode 100644 packages/next/src/request-handler.ts create mode 100644 packages/next/tsconfig.json create mode 100644 packages/plugins/react/.eslintrc.json create mode 120000 packages/plugins/react/LICENSE create mode 100644 packages/plugins/react/README.md create mode 100644 packages/plugins/react/package.json create mode 100644 packages/plugins/react/src/index.ts create mode 100644 packages/plugins/react/src/react-hooks-generator.ts rename packages/{runtime/src/request.ts => plugins/react/src/runtime.ts} (71%) create mode 100644 packages/plugins/react/src/serialization-utils.ts rename packages/{schema/src/res/tsconfig.template.json => plugins/react/tsconfig.json} (52%) create mode 100644 packages/plugins/trpc/.eslintrc.json create mode 120000 packages/plugins/trpc/LICENSE create mode 100644 packages/plugins/trpc/README.md create mode 100644 packages/plugins/trpc/package.json create mode 100644 packages/plugins/trpc/src/config.ts create mode 100644 packages/plugins/trpc/src/generator.ts create mode 100644 packages/plugins/trpc/src/helpers.ts create mode 100644 packages/plugins/trpc/src/index.ts create mode 100644 packages/plugins/trpc/src/project.ts rename doc-serve/pages/.gitkeep => packages/plugins/trpc/src/types.ts (100%) create mode 100644 packages/plugins/trpc/src/utils/removeDir.ts create mode 100644 packages/plugins/trpc/src/utils/uncapitalizeFirstLetter.ts create mode 100644 packages/plugins/trpc/src/zod/README.md create mode 100644 packages/plugins/trpc/src/zod/generator.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/aggregate-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/comments-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/include-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/index.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/model-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/modelArgs-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/mongodb-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/helpers/select-helpers.ts create mode 100644 packages/plugins/trpc/src/zod/index.ts create mode 100644 packages/plugins/trpc/src/zod/transformer.ts create mode 100644 packages/plugins/trpc/src/zod/types.ts create mode 100644 packages/plugins/trpc/src/zod/utils/formatFile.ts create mode 100644 packages/plugins/trpc/src/zod/utils/removeDir.ts create mode 100644 packages/plugins/trpc/src/zod/utils/writeFileSafely.ts create mode 100644 packages/plugins/trpc/tsconfig.json create mode 120000 packages/runtime/LICENSE delete mode 100644 packages/runtime/pre/client/index.d.ts delete mode 100644 packages/runtime/pre/client/index.js delete mode 100644 packages/runtime/pre/server/auth.d.ts delete mode 100644 packages/runtime/pre/server/auth.js delete mode 100644 packages/runtime/pre/server/index.d.ts delete mode 100644 packages/runtime/pre/server/index.js delete mode 100644 packages/runtime/pre/types/index.d.ts delete mode 100644 packages/runtime/pre/types/index.js delete mode 100644 packages/runtime/src/config.ts create mode 100644 packages/runtime/src/enhancements/index.ts create mode 100644 packages/runtime/src/enhancements/model-meta.ts create mode 100644 packages/runtime/src/enhancements/nested-write-vistor.ts create mode 100644 packages/runtime/src/enhancements/omit.ts create mode 100644 packages/runtime/src/enhancements/password.ts create mode 100644 packages/runtime/src/enhancements/policy/handler.ts create mode 100644 packages/runtime/src/enhancements/policy/index.ts create mode 100644 packages/runtime/src/enhancements/policy/logger.ts create mode 100644 packages/runtime/src/enhancements/policy/policy-utils.ts create mode 100644 packages/runtime/src/enhancements/preset.ts create mode 100644 packages/runtime/src/enhancements/proxy.ts create mode 100644 packages/runtime/src/enhancements/types.ts create mode 100644 packages/runtime/src/enhancements/utils.ts create mode 100644 packages/runtime/src/error.ts delete mode 100644 packages/runtime/src/handler/data/crud.ts delete mode 100644 packages/runtime/src/handler/data/handler.ts delete mode 100644 packages/runtime/src/handler/data/nested-write-vistor.ts delete mode 100644 packages/runtime/src/handler/data/policy-utils.ts delete mode 100644 packages/runtime/src/handler/index.ts delete mode 100644 packages/runtime/src/handler/types.ts delete mode 100644 packages/runtime/src/request-handler.ts delete mode 100644 packages/runtime/src/service.ts create mode 100644 packages/runtime/src/version.ts delete mode 100644 packages/schema/.env create mode 120000 packages/schema/LICENSE delete mode 100644 packages/schema/LICENSE.md create mode 120000 packages/schema/README-global.md create mode 100644 packages/schema/README.md create mode 100644 packages/schema/build/post-build.js delete mode 100644 packages/schema/script/post-install.js create mode 100644 packages/schema/src/cli/plugin-runner.ts create mode 100644 packages/schema/src/constants.ts delete mode 100644 packages/schema/src/generator/ast-utils.ts delete mode 100644 packages/schema/src/generator/constants.ts delete mode 100644 packages/schema/src/generator/field-constraint/index.ts delete mode 100644 packages/schema/src/generator/index.ts delete mode 100644 packages/schema/src/generator/prisma/index.ts delete mode 100644 packages/schema/src/generator/prisma/query-guard-generator.ts delete mode 100644 packages/schema/src/generator/react-hooks/index.ts delete mode 100644 packages/schema/src/generator/service/index.ts delete mode 100644 packages/schema/src/generator/tsc/index.ts delete mode 100644 packages/schema/src/language-server/langium-ext.d.ts create mode 100644 packages/schema/src/language-server/zmodel-formatter.ts rename packages/schema/src/{generator/prisma => plugins/access-policy}/expression-writer.ts (51%) create mode 100644 packages/schema/src/plugins/access-policy/index.ts create mode 100644 packages/schema/src/plugins/access-policy/policy-guard-generator.ts rename packages/schema/src/{generator/prisma => plugins/access-policy}/typescript-expression-transformer.ts (56%) create mode 100644 packages/schema/src/plugins/access-policy/utils.ts create mode 100644 packages/schema/src/plugins/access-policy/zod-schema-generator.ts create mode 100644 packages/schema/src/plugins/model-meta/index.ts create mode 100644 packages/schema/src/plugins/plugin-utils.ts rename packages/schema/src/{utils => plugins/prisma}/indent-string.ts (100%) create mode 100644 packages/schema/src/plugins/prisma/index.ts rename packages/schema/src/{generator => plugins}/prisma/prisma-builder.ts (55%) rename packages/schema/src/{generator => plugins}/prisma/schema-generator.ts (52%) create mode 100644 packages/schema/src/plugins/prisma/zmodel-code-generator.ts delete mode 100644 packages/schema/src/res/package.template.json create mode 100644 packages/schema/src/res/starter.zmodel rename packages/schema/src/{generator => }/types.ts (56%) create mode 100644 packages/schema/src/utils/ast-utils.ts create mode 100644 packages/schema/src/utils/version-utils.ts create mode 100644 packages/schema/tests/cli/cli.test.ts create mode 100644 packages/schema/tests/generator/code-generator.test.ts create mode 100644 packages/schema/tests/generator/prisma-generator.test.ts create mode 100644 packages/schema/tests/schema/formatter.test.ts rename samples/todo/zenstack/schema.zmodel => packages/schema/tests/schema/todo.zmodel (85%) create mode 100644 packages/sdk/.eslintrc.json create mode 120000 packages/sdk/LICENSE create mode 100644 packages/sdk/README.md create mode 100644 packages/sdk/package.json create mode 100644 packages/sdk/src/ast.ts create mode 100644 packages/sdk/src/constants.ts create mode 100644 packages/sdk/src/index.ts create mode 100644 packages/sdk/src/types.ts create mode 100644 packages/sdk/src/utils.ts create mode 100644 packages/sdk/tsconfig.json delete mode 100644 samples/todo/.babelrc delete mode 100644 samples/todo/.env delete mode 100644 samples/todo/.eslintrc.json delete mode 100644 samples/todo/.vscode/launch.json delete mode 100644 samples/todo/README.md delete mode 100644 samples/todo/components/AuthGuard.tsx delete mode 100644 samples/todo/components/Avatar.tsx delete mode 100644 samples/todo/components/BreadCrumb.tsx delete mode 100644 samples/todo/components/ManageMembers.tsx delete mode 100644 samples/todo/components/NavBar.tsx delete mode 100644 samples/todo/components/SpaceMembers.tsx delete mode 100644 samples/todo/components/Spaces.tsx delete mode 100644 samples/todo/components/TimeInfo.tsx delete mode 100644 samples/todo/components/Todo.tsx delete mode 100644 samples/todo/components/TodoList.tsx delete mode 100644 samples/todo/components/WithNavBar.tsx delete mode 100644 samples/todo/lib/context.ts delete mode 100644 samples/todo/lib/query-utils.ts delete mode 100644 samples/todo/next.config.js delete mode 100644 samples/todo/package-lock.json delete mode 100644 samples/todo/package.json delete mode 100644 samples/todo/pages/_app.tsx delete mode 100644 samples/todo/pages/api/auth/[...nextauth].ts delete mode 100644 samples/todo/pages/api/zenstack/[...path].ts delete mode 100644 samples/todo/pages/create-space.tsx delete mode 100644 samples/todo/pages/index.tsx delete mode 100644 samples/todo/pages/signin.tsx delete mode 100644 samples/todo/pages/signup.tsx delete mode 100644 samples/todo/pages/space/[slug]/[listId]/index.tsx delete mode 100644 samples/todo/pages/space/[slug]/index.tsx delete mode 100644 samples/todo/postcss.config.js delete mode 100644 samples/todo/public/auth-bg.jpg delete mode 100644 samples/todo/public/avatar.jpg delete mode 100644 samples/todo/public/favicon.ico delete mode 100644 samples/todo/public/logo.png delete mode 100644 samples/todo/public/vercel.svg delete mode 100644 samples/todo/styles/globals.css delete mode 100644 samples/todo/tailwind.config.js delete mode 100644 samples/todo/tsconfig.json delete mode 100644 samples/todo/types/next-auth.d.ts delete mode 100644 samples/todo/types/next.d.ts delete mode 100644 samples/todo/zenstack.config.json delete mode 100644 samples/todo/zenstack/migrations/20221014084317_init/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221103144245_drop_account_session/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221126150023_add_account/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221126151212_email_password_optional/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221126151510_refresh_token_expires/migration.sql delete mode 100644 samples/todo/zenstack/migrations/20221127033222_email_required/migration.sql delete mode 100644 samples/todo/zenstack/migrations/migration_lock.toml create mode 100644 tests/integration/.eslintrc.json create mode 100644 tests/integration/global-setup.js create mode 100644 tests/integration/jest.d.ts create mode 100644 tests/integration/test-run/package-lock.json create mode 100644 tests/integration/test-run/package.json create mode 100644 tests/integration/test-setup.ts create mode 100644 tests/integration/tests/e2e/prisma-methods.test.ts create mode 100644 tests/integration/tests/e2e/todo-presets.test.ts create mode 100644 tests/integration/tests/e2e/type-coverage.test.ts delete mode 100644 tests/integration/tests/field-validation-client.test.ts delete mode 100644 tests/integration/tests/field-validation-server.test.ts delete mode 100644 tests/integration/tests/field-validation.zmodel delete mode 100644 tests/integration/tests/logging.test.ts create mode 100644 tests/integration/tests/nextjs/generation.test.ts rename {doc-serve => tests/integration/tests/nextjs/test-project}/.gitignore (100%) create mode 100644 tests/integration/tests/nextjs/test-project/.npmrc rename {doc-serve => tests/integration/tests/nextjs/test-project}/README.md (100%) rename {doc-serve => tests/integration/tests/nextjs/test-project}/next.config.js (100%) create mode 100644 tests/integration/tests/nextjs/test-project/package-lock.json create mode 100644 tests/integration/tests/nextjs/test-project/package.json create mode 100644 tests/integration/tests/nextjs/test-project/pages/_app.tsx create mode 100644 tests/integration/tests/nextjs/test-project/pages/api/model/[...path].ts create mode 100644 tests/integration/tests/nextjs/test-project/pages/index.tsx create mode 100644 tests/integration/tests/nextjs/test-project/postgres.zmodel create mode 100644 tests/integration/tests/nextjs/test-project/server/db.ts create mode 100644 tests/integration/tests/nextjs/test-project/sqlite.zmodel rename {doc-serve => tests/integration/tests/nextjs/test-project}/tsconfig.json (100%) delete mode 100644 tests/integration/tests/omit.test.ts delete mode 100644 tests/integration/tests/omit.zmodel delete mode 100644 tests/integration/tests/operation-coverate.test.ts delete mode 100644 tests/integration/tests/operations.zmodel delete mode 100644 tests/integration/tests/password.test.ts delete mode 100644 tests/integration/tests/password.zmodel rename tests/integration/tests/{ => schema}/todo.zmodel (82%) delete mode 100644 tests/integration/tests/todo-e2e.test.ts create mode 100644 tests/integration/tests/trpc/generation.test.ts rename {samples/todo => tests/integration/tests/trpc/test-project}/.gitignore (91%) create mode 100644 tests/integration/tests/trpc/test-project/.npmrc create mode 100644 tests/integration/tests/trpc/test-project/README.md create mode 100644 tests/integration/tests/trpc/test-project/lib/trpc.ts create mode 100644 tests/integration/tests/trpc/test-project/next.config.js create mode 100644 tests/integration/tests/trpc/test-project/package-lock.json create mode 100644 tests/integration/tests/trpc/test-project/package.json create mode 100644 tests/integration/tests/trpc/test-project/pages/_app.tsx create mode 100644 tests/integration/tests/trpc/test-project/pages/api/model/[...path].ts create mode 100644 tests/integration/tests/trpc/test-project/pages/index.tsx create mode 100644 tests/integration/tests/trpc/test-project/server/context.ts create mode 100644 tests/integration/tests/trpc/test-project/server/db.ts create mode 100644 tests/integration/tests/trpc/test-project/server/routers/_app.ts create mode 100644 tests/integration/tests/trpc/test-project/todo.zmodel create mode 100644 tests/integration/tests/trpc/test-project/tsconfig.json delete mode 100644 tests/integration/tests/type-coverage.test.ts delete mode 100644 tests/integration/tests/type-coverage.zmodel delete mode 100644 tests/integration/tests/utils.ts create mode 100644 tests/integration/tests/with-omit/with-omit.test.ts create mode 100644 tests/integration/tests/with-password/with-password.test.ts create mode 100644 tests/integration/tests/with-policy/auth.test.ts create mode 100644 tests/integration/tests/with-policy/deep-nested.test.ts create mode 100644 tests/integration/tests/with-policy/empty-policy.test.ts create mode 100644 tests/integration/tests/with-policy/field-validation.test.ts create mode 100644 tests/integration/tests/with-policy/nested-to-many.test.ts create mode 100644 tests/integration/tests/with-policy/nested-to-one.test.ts create mode 100644 tests/integration/tests/with-policy/post-update.test.ts create mode 100644 tests/integration/tests/with-policy/todo-sample.test.ts create mode 100644 tests/integration/tests/with-policy/toplevel-operations.test.ts create mode 100644 tests/integration/utils/index.ts create mode 100644 tests/integration/utils/jest-ext.ts create mode 100644 tests/integration/utils/utils.ts 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 b2074d34e6ec7cdbec122eb1d8eead3e182b1f4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33040 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νSO<@ip0OQ66_uoD{O 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 z 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 G|Nj6F5~m0N diff --git a/doc-serve/public/_media/logo.png b/doc-serve/public/_media/logo.png deleted file mode 100644 index 7358d53b67caed1c6a1d7b76eb588d38840c632d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23566 zcmb??WmBBb6YXM)JAn)EZEooA-2yXW-jGtnwa( JC;$KeT~g!P9&j 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 diff --git a/doc-serve/public/_media/starter-shot.png b/doc-serve/public/_media/starter-shot.png deleted file mode 100644 index d6531d8b1ee99fc801c2a66b689ba432e3c1ed4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89412 zcma%jc|4SD-~JF~sgSKyh)TAml|qe*R6?bwq|qWICS+eOD%rB#?wZOnTGcHk`|h$Y z*-2v``_7DY#w^$MJKfLozVGj!=ehkw`pnFAp67QtzQ=K#6LQJeV3UxH5C((UbpD*) z6%1wrg2C`62&@Nx`CgUr9{k{Oxngh{liIjr0)vsqoYy;b)#LVbk?@lp6wfiwD$JR`R#U+jW}~Cd%-yF zrDkM9W4%{XV};wJI*}XceIJG^BJRm4#qwor_x|JhmW$6m2t++^?#0MhdKX(PN$wYF z?`;o%7$pz6>RoefPqDZL|Fz$r1EJ_r{cLREDU!1&pQVV?bVphM$$%1*$ipX(;Stcq z@QV6l{^PF!VZkNQiU#?iSqV9cR~#q{xluo80?~0;kN&Zjbkf4kkJ|tA7Jq-nHUW8# zjsKsm_xlyRprm#uascUY8H$;eu{{0XAK+h4IDQ&40K*3VdbPE)Hu;Dc)O?Vek&%%X z50N+>oBrz}{=Lc){_|E|US5d@S$37{j*EyKXkMU=eX;z?>*_cbNT)X4D?Rz&w&H*H zYW*3^01isc6P!Lyy1DvN(n9?EwQWPUygwb@^t`^CS-7=#}R zX(@WrVbJYBT2}_i{p#dj%O>fP_(YK$|AO6tXmKT?qlftI;GVBW+~QW0jN``HEHJ%=7hr641IF7fOvBdCx)=0$kGMvN^c2WEEhX3z1 z|M6F_R8gcsAIo9vVHVs))9zJ!O)4fAP+b>ooEF9gy70GdO;6lhk4g>;tmz0@iU%|_ zvK>mb4O{$Ug`$p%B~m0Xnmi>7tkzo? zn<|BPOR@P4a8DskBBU7vX}Uw225>%e;qfe1U45xCW$L5ex-}*Eaj~yv$=y;JSyqV_ zTDm!U{%*>c8{(MseAMMJVsTz4$>PVi*i#|(Cm7wy{qW8-IHs9uJWSK?p{9Dkg@rw@ zsin_0Y?#~8thAICC$(m1uYmugI>?Ot|wnPVu-7d%B0`8dY<>$c7AG+M*i1{nI6xK*;U5T*{UnV)3s+^rYi4v4o2RPw2iTsSSk&B$eE2q4{JEHV0F9lIj| z|GB{aZ^*m-sf)c^9Cw`@h=~`%Xnn>^pPs{+Qy>!sU!u~Nq+%~Ij&>z0$AxL*$_T5W zHnvlZd(1Bv$J@iwEycIVjEa=r6K~?;dRnkYHauI)=ivfkluSmQ#YUWuXGHa;dgzAn zbvY2sC8A!hkIE92uM@@eZoybn43>Jhr4pyM3SxGO5maC`yIXk7XrPI*2@oY@`1 zQW<2Z^l8e3C%lHn*7|TbAk;l=){q#pG<2PZ5=GQt%H63AuVU zeha3N$fU+RCgr|;%V9CtO%COrRDU;kz7tH*>Z( z@~q`C!V+|^B;s5~FH-Dh&D5f%Gb|747M#ZHRIw`H$1E%?q~-QG2>9z%KuvXODZ)d- zrp%_vWt#aSO=^l3ip1|g&bvcb=qBRq7)RvD<&#D|<_BRpF`QwVE`_jT(;A+DN8pKk zvr&af_q9N7rb0j_(kjIB$J8JCm_PUu`7zF$9mRB$`22@KX7r#NHtz`LXZls5CoNI! zy&M}AgN}%Ptme5vuFZo7gJXNP%}~$N2};XhblOROm1U)PZfXs7uuzq*ROw|NEPVQ)&gSn=8g_o^MSkYcqKACymKgjGpcqM9UuSN+a$@d6oYT=C*&u5D#5F~(r_>l zA8Y$<>DFnz?A;RihWbe3Fje~Yoi#m9tAK=1;6-D|9WS{=f{+z;%P)dRt!wz%du<0} z_<5GRPV~(>nljM&dFR3thC=>QJZk&*SFuNCkftk`e$Nx<3*XKfVoyAH7)u<-n!MOf zBex)JZEd5A6K!*OPr{aHdzl575fx@~BI)I5x>tgkoL)DS7nd4|&CED{Gz4#RFn{zBf-QtiNdgu`% zlyx(l{JzDuA{CjNbMo$r=L4G#t4=0`aO_U!e>W7LXG`|7d&wm1G4@)jwe*GnwWoev zufEJE1@hWW#Z3KCiAn9CrU>X(Ah(4vuV26Z?s0oR@t&Ba5GK6}Ef|CshomrzDu~>1 z=Zb_gk*QGU=X*T5QkQXtvB&!G5gq#nVe=NeCDNLt$|~58x}_m8F)?4vk~>|R6qXAP zA}Y)FPhi#QDd)87FY|WmyPN+aD6tCgo7FKj3Vk+qE>4AabA{FbPx^htYOaVTI`rJf z9H$x&LN}fKyZx-KD}{RODlu9|{A2kN%X#jf=lOb7rL;CiDMtcRGuJwZGH6t}`o_kp zoWyuGjGx16&A!K2*#x3)AKDjqFvzaIABBq&%9+|^0$CF4ozKfF_YHbZ_?1+GD$tx)w;_gk|i`qTlTX_?^Ko10(~CGgZ^4*%m>KX_oi4$ zYwHyq9sghid`is~Omo<3AVErTX!jQ6+9EL)qyW_X0cstA)6)mTBO?jUi%LTrOLyo{ z6;*P{k_g|rmyVGKmAOSnc~*(vS8UF5t~kD<&*-^{6y}LN+>6y>pZUKDza8#mG_1+Q#u-p{Z5g6FX~MoLQR zrNnw$4%Et}Dzk8eIkYBP(aA~w>==YZppK4cAwYnckQ5QlWgTtB??le8?)>=;u#d3? z#hBx+?`Pic4t6}sov5E{B0dFH)g`T(*Ca2FaYOCHRR0U@@V}_IRbV$i>S)Jnr;JID z?2haX+OHJGdzDVP>wkZj?v;Lb+*I+bmL|V^3Z}#e6J>oT`Xy@Of+p#sA{RI~Y#BuU z0Q<)#Bt(d>H;y|dhIJ2Rn22+|T~`XCk<{{42sttBj!W)8siX6#3D3T?ilML)LUUPr z`F-t3?O9ZS^c!vN-`|ZC45)I>HiZ zRVzyTh;!bfK<<$=obsZ5?2%}yB*XGxpX9v$4_fsKSyP`aBP%=0;{UuB8tAc|_;m}5 z3`|!D`@Ggn9OB3m&|T%qnsQFMi!s_1r7;tVnOw?OmwhbE`v*UUbwddAYX!S-_TwFX z7iEm-9G+OoCAXmI#)D|v2b3*_M#|wF_wL>6wFNyafX=3LZrv;IRGu${PA(F;=|X7g zIF`L95S^H?b73voc~5`-yhochylCD6J$Z6O3XN$&&J*G7Q`ozHpRc5SDCRivLpfV@ zpL#-#|z*ZC2bRU?vv{&74inN^C~`zyJF zIx(pw$-6t}JT#mCiB8t%$uG7%=pTyY7g>t=Ywz%%Vo|Kcxb|7^qVqWCLC2d#9b3?% zQ1srtduaj?pg%m08*yl(EFwE54P(VFgK!_91Y<~yTRDT3RtFHi4qpM8(@k8M$seh% zjDowcTCzBPbaGsp$5P*~cmLEaC%nF!`7e`Dt}~+0ti-?K#_Nl1^wUFn(bJ=j^{D8Cx>0SPIfh_Z+cI}+>995DX^y0{h2X&fsPf2SSis@n}Fh#`z z`?ik1+B&oAABo5xE_~?%%o|O68!AaXnnu&^-QUUN>c)lpyt?n`VZ_YrK-q+`gSX1=3XW_;I_3S?`FWi_{zf{uby-*|K>wM#6lBD3LqWz-FO{n3YhyS^1mjoKi8{Y9( z9i3X*y_1`NvY`{(eM=LP8lW1jJ7>sy4m7R{7~3&~poD}qL!P(g3k`?hQa?0a`$xMy9$gUNp5=!EfhTu__-8fcSG|$6&VgmS{XbIxM``BpU8Lxc>`L zhQj-g%{ZXiVdA02MFCwohPZETti(0bE!I*+D4qbTVL@?xuA-JR);; zG?k4fx)xi`L5_}%wqpKONK;djMX6tq0-B@5QPPKwJFS*5R80(Rf7ot+L?-)~)srW` zV%|VbOj>u2$#d_5@5YeR3OOakA#_=~1)yB!)YO#IOE{Q2IS(+wQds4Ot8bZhjebtk zVg2OGh!#PKQ|FoQFp^=hFOj5wQF`Fy#Jox9O1@>6$fivWEXOZ`^YMDDVWf522R|HH zdkwr+$*e+@*^VE@#eK^PtzAl#S|q#GY z@{Lme9L=Izj|3dKjE{J0hKah!Z<}>f1_vLkDjegt3*>RU1uv3Gz19;{$>p{RWH?`5 z;HDjWg7qL{+xTP5O+I;2Zw+de-0qA^3PGI{YCF&?-|mwOVwt3%DV*j2I^OcpA&B>J zAmqfVt8A#Re`D;30#wp%tOvafqhmjy!^5AxwLkm~$`NQ7a~%WNGFX)rR)_CFQftV@ zQ22^85!Pde*`|JgCDsGXH^?#xW!gES_b-F~g{Ay)aYK}AtJ!lO$;p^Ozjx)hl&$iU zQ1@l@#tzi(JkK+~v}W8Ic=4!kU8S+&GQlS*IT4$#J7`6Dj_47B`5aEE5-2 zK_Kr;1=lSVa$*-eg$39uNZs6?dAup6VuCcvF{whrx~Y8c)KH=^c z78d5#`|H=A@yXrzTgW7nCN)PTx8e2Cm6eqin*`6%(9fSg&v_LUliMGLuRnDun9swT zd#JYOhEmd0Y7S{6Qgu?B{Z*WL+vfBl#dqn1Rrj$*^w&%+@oZ=?xmu=z{n_DXx%}p{ zq&4tdx+Z*D`LOq-^245oSx<6|eBIA&e%u|BOJKNn5jqppghV@!q&3bCNeI>0%vkXG zu#an9wzwpEfjsA>e#n+p4A?ee)79kmz+`eUtMl8pZ`P9N!eayrz#avT3sXhHhVZ?s zXFCHZf^{1D38C( z1@-*cQceXs$Awkr<-~$!?C}L|4@#P8oM!f<~5$td)`BO{y@6<;Vn9W;dB!TI?3 zC7pY^Ru6|WXl0z|I`Q|P;x+b$M%)VxzbhqBd8l-Wl0sSMd&LuDH+xk5UDn05iqWW? z@h>=dWy!>Z#UM58_Bb=lxc>m_9N4s8N$>A+*}HbB*O^Xdp2*3Fm~E& zhWI~TfUz->$6Oem^Jg$RNpG6u5RS-{(&>*crI=aRG{zm=f?}Oe6+}r99UC2e8H}oM zlJt>eCg};6_J&T9n!}2m(H>ac@gz7HO^?jyZV!JU$*Hcc4i`d4oI9m3!Q=Skc23>? zf{R^jMMcHi8J&yw`)rR4HVx8O8>}_*%hV(ssl;e%db9Ol)@3S<1q4A+9a>!<)EDcXM50X6BpVurR^ogt-93yO9NvrP@H+k%4)_c=Yr?hC2Jo#(c z_n-&+4_&JS-`P9@QX6C9H*$jJ*+k-&h;T~EDYw06(kl>m7lc?h@_V5PM^Jfn0Td@O z&eT$Vd>9@6@x$u=elYHU3UCWm;M5&Cc(A2qh3ftbZTVG997ptj!rfsZGorb40lF}? za*h~kxMY4Vfx199&bun7ajK|WHn-QUm~~8qxvj2<-J`7|LY+D(Qj7Ci7Id^Iuq+el z$xYiTopnyZw z{!wzb`(UuKt!-|L=3YS2$p_+`5>4i0pN?w)~P=C)D;IQ2oi@m0ZX-bTNu`e?iMt?wK;N%3>L{1Z3DqdTCF zmj>?+(MH(|>~pWp7c{ys9^2^}w4b=Hu@{G240MX$UUqe;!Tz!o8|kwea*yvRC?3}8 zNy+ET=3Sn6?zv&ieZ#iRyK%24PJ9+f(kWT@Vsn2XW?{YYA3HcETpvin*`-Z;H$yE9ExC4puC?dl=YO1%vZc`xES4nehsx91z)zkjdGhXUxA{#_X} z$)qS?#L(UvD1-@mw#GcJVF_!#w+sgA)e!#o`C+sq5OTjb12BvOD`Br9GVQ6|`ZFtLTR&8q=nARPEq6Nkg)XJ;?7sH_Gm zi}aT_);mC;!OVw3lJWZT+aD{Zp~E!$nG*>7M1t9adj6MwWIJ$woY&FNUEKI0@|Km) z&SNDi=pFnHw6z~z+0gZwHRRBYW&A)5U(H$}lNxVsmuo7B^SLFId}jG=6`1&iJ@|a# zUW3JE|4vbr>qZreBlg)HXqBu_HHhMT(qe?ID)5p6Z~<+=!q#5yAd^8 zX)=c_Z)!0b+_@D62yI|sz~rKlk-BEAk<(oDEc$62TZy@$h!S7Fo}M7qS*x;|8X9ge zU@*#3U@gR&)Y;joZen(5{%=-!yQcp6;?Oxtn)CoQe1#YTptdU;kTBWb+VFr)oWY8r z=|=Yuz$N)OvE0L$Ldli|Gs8?w@1RxlaiSa43&-3Yq+8qEbj8M`!*Ry9r%;}qT zM@640M9Gxb8hTvH{Rp}7Ja+V7*+ty`b1|X|Gb+{??w!Bi*=}9q(xkiYv4$ld7w`0J zwLY^6#_jGdu}kjdnnUrShusmd!}G4vXf=vrF?QI=>zXmzBVCUA$v%BJp= zL@Yt$(lKHJ8r9jk_1D9!i;k#Q(QM?)m**_Bi8Y{J0LGyLL@#k=C=!R5;5Y*!=&vkn z?(znG7jtee(x^Ce^0~geMm9B41=B+f`hHbzV(IcZ=sT5A$Q!S~n|G<(rm(HbrFIT` z0`X$3c)q1-Cy$r{{@|Rv)@&qEo!>RLeB)-V)O| zO2dGAaN)VurohwwIcFl=pHf?#pXX&IXM0>ua2{?v6Q@}J$Z~E=V^xTmXl{ofoz*+1 zT|ec1phojZ&n2|U5ao`Vn)oiM6Flq4a#~NuvHHW5H|X6o+jtZp`Vr^6Jz!u#0QUN0 zi|DT@S8Y&6RbO3rJU?99u|(R-5mpc9n9rew0;tTwD%tdA`vMuHIj4ON{IK(0p1uM3 z`1E}L{@vr%g-VN?H|LKI;_o3x2dUvK=~Da-L;*3Zhc9kfLOYxMmRUh^e$4TNhf6={ z50VeSVs=*MwN=f(34sV*_-pu4RCg!)a_li>-8iK9;Hf%3zdwZ%n+Q+p>05U|=JPa$ zWrawiUu~M)KhJzxTYj|8ViqZ2`XErBv>6%Txs&F&UEsl4M`!U{FrPrfU=lS{ILS;r zfSXx28-!3{Vc`d~z9RKJc~@>-x&XR`fvwxJLVC$2E&&ueglw2)nh2o^tUAFg7j|9t z3GZc(-W~+J6e|^$Rsi(c)FJ$HPyqGm(gQ%wXn<+OY)9^(G`v_BSa#|1RqlaQygqX2 zZPwJ_mDo)qxQ9DWTzX%vu#x}VuaV~~=pF4DZ5dkuJ(LMBJ7_Rwqf990f5B*wNSnHWBTXG|3l%K^L|2zPzvDpU1y zBt0I4^VG6*5STd*)u!ul!ss9mZeZ(fVjfd{8NJA7tnZ4set*thA2IG8yf8dyaX|e^MsawLXZMg5&9APQE^3 zSm1CRX?wm(r<(xE(*GEuoyP^U;^%N`;|wj76W@aFkj}k?^%&j3PzcNYZ&bj^$~tN?n%a#Y;uhp_ z^_kMus5H#1#JBeQwlm9v(dH>^%xj)UYR{X3!&K=IK6* zNH2YN_a8F2XP~F+CJqwyQ|yMI@f_*pwU{aY;46E{2v6%-_$7y*3pMA&O))`=-bLGP zp)zb*K0={TKKshMH~~(COVW04{3~gY2VD#vb6)0Wwc4qt#?IBWV_|Nhi zCP4Uw|INY_m4pn04c527_;ehxq`(5meBO1nuBVug6?aP@@K!(}9O*fHD5fVDIz9s! z(rq$UhCT2jdnJw>&abEMSxAVW_XKcMIm!6<#T9=*yaDPM#rGn>%_2Sk$9LnO0}*9Z zI;>KC5WcqJix=YKoPOUQ(o8NxySi@P!ie&fHjm-Z6?5MKVY~-iRCB32@*=9xXSnm+ zKD1iog}{=VTJX6sg1!?bTW|M)1i8IDj}q>l*VcKlXsaC|^Uym2)?Q(-SSTj%8 zP=zr88hq z`)Jk6+`gWB>?y8sUGVR(_P8x`N853~7c@ii?{xbA5(pk7pTTsz=e_@``}GiLd)(r4 zIa0xk-Qw(1Yp{Xr5Ae9U%rj0V`;4ekz3bF-d~Wk0r{6a_3uA6kV6}qFfc3+^y6~He z-%qqqK-@#wO@IVB3XTvgVwx%PXxaA|pf}WR12=l_ZqHRmJ4@{{Agun~O1upX4YL6& zHMmTQi#r06`9oMN$nhaAW2RflX}#agdU*Q^ZNE}4s6hqMe`DGdlanifjLHO`sTvUn z)GU>_=*KQq-VnVHYDZI|ur}NBnKPn1QsF)V>|j*x(W3O`^`hZwVj7!b)*k~vgi2FA z;@7#@&9kQUPw+i`e4TM>oJ=X2e?=<-6NBufoYfL*l8;yhK?T;~Y#?i=9JXoP>cm|w zlFD*&a)fvt&sN(Wba3!P(QYR$aO30N#K&h*W`$6pdv0!Srp86vSD@jVXpdM*G9BOr z+1*A=B2Yk(&*9q_{x(T#P38q(I zUz8PpEH)@BvXKkER7|vc=Kqytd=_?0^F7uAq(6e3+O{q7GJEB<&R(}J0pJOgsW11r zjJ4s3x4LD%@gEWIURN?F(1K9Z4$g^bK0sCH6Mc{WfE?w~jYlT|=?nTMkw|nH2K0^) zhzfuYR{%U#6?R1c9Z7AN0bJwmVrnP=yyhyB+R~R-Az<4Zh|-I_yxM&CqhE_GiBH&d zW2?)0t(4#~cwOMGVh4`1`zicLSrI?Rusd3VQnx zQn`ek(w_!%zS$zEPtVZ{pSL^#cwq>CXF8V-EX>x{O_^`dhglDE;!k-028W(Ahk$PK z9Cd3!K*v-;K*l~tgJiwN!GezY{ff9{6)CgTX{Gy(-}WI`tX6I_Prt(_qB?d!vj-rOj9QhU%j@+0ptWd8_h&Nc1xjfW@B z!|`hw*W^XT>3Tskha4QmRC0S6heltN2SxURx$RpS^gDM|briF?H=KjgYeQIL%z`K=NAqmK%n7s2OWs@8 zfRjo-pVhtE_uce}g3s^KQTnDJXzsTK!S~!C=i0bQqjhW*x9*k#>Z&3kbjmre#g-48 znIojUUbGrk<&Z!P{CQXC2h@y(J)e7BMhAa^HXFX8A7fk5pTm2-tAN#&5%JnFrsbCvymETD6hY zrH4HOxOzhna-@pmfM zenodWaSF;%fE8D#)uDfl=Am?275FRg7WCdldU^dn^^-|*zKbofa-PG{q@2jYQF`6e zV=JGhbxAJ{bmERITeSI&_Y-$vbZ|Rlbqg+;pra?n@hY)&xvhBCfq-rAynk-wF5zVs zmf)j(wVvW}C&Xuq-ARIblU{lbxX1oxa826^SzQ4;;O-@FL)3~>N#07E#&2))=z7yW zRwgWy*Yl?J=$9-8bfA=J_%d%=`?QW%-ods;3U!8AmLrZPy+%uYW~R*>s5&_Ngxf2x zkLS5{xg9@VNWEoR_|u1G-8Ne34K?4gSc#zPZQ0@K~h_H62a{RW3%49Pndr3j7td<@iwl1xmuG}GlBq*MiV}PSOJ8om4323PG&)qJ?Mm*1 zDQ{XXRKAIRT5;g{SWdBH{lhjj%EQsc8~1J!sdt{*Q*Cb&E$y39x|iIw{WKAmtfYD+ zW4~6P2LocJ+vN^^e0BNo5uHbzan8-DsRzCe4QiCvM%H)Q3mtmBHH?0P8M=itIRM#RHg}t`%UKQW3>s2k>{15#1}8A3#J3nU_BG zH!J}(06-!v;KS*(1&*S;J%jk)Vc_x&9L#p)@mF0|3PcTLJ@uZWjE?VqxGgM)OxWh8 z!l3Fl3% z>uPNd$b=?(j(%TRzEJLg`~EH5^Cqo`{pj>F zIhB)upm*wp`WsVbc5k9>26O~`a8mk9Eig+$1k->u~~^*Y7oSV8mk zkD9c4J=%=qa_6eH7YLy$%(`28kn5@neT}*5`_bdz@pwWAKn;)>3OWk78v%H=PX|g8 z#s>%QCW8KSjVYZ2$6Zny0EVDiU?wEJG;cOzN|&zU+fd}tw|+P~`++d#eo;QMyU6P8sMT;oXTLcDS-b9O;wfC!^b2w~3b7|7yq!Y3F03y3UL)okk^?=C#f1DC%QCa9ttCj!8*3L{D1O+biMa`VQN8JWZfMzcqE! zEw{hsy8RPK)ht)E_#ZF8-_BfJy%OOK`7;ejdVlzr%sxxqCKwf~qy+aNqCimu?0BbJLXUmYat;N+$2pBDvD0@o@(p*dFtF zq#^jbwz^5II}2mw+n^S&V=YBxlItgN@W~yg<%i~mm>k`veffWlvx1CI-d%>+V+{$a zUtVrg(MuZJh7EoZa!%-X^thZyvGx6=r7y=B(}l-`&&>;zwEBpz?yQF)ocU z(rKT64z9j~`)8W*)F-4^)6xzW_*q>>pNS$Vt#U@d zznZ%0(JzB7F#T0r0J`pJo?L7`gR{4GxyCKm-Ya}gn?Y;`aXj-*EZng}P?oPCL& zEri`9&(giW&}WXDGCwPh_9Hdt;fY;cTOV50NjD`JT*V}s*nM;yH703H;oR$wkPB6` za>j3scnzxgCKq%M*=OoLd9i#z!DDKPodwU)@)r*h!S^@xPIbBnamOT?!e%sKdP zu}SdA+ffFZAb6JC_=FR#_3jBmeOUiC2bs~nI5QclOjNPZgDgCv5Ak0<^;8w1Y(Hx1 z`Q?EhkfgGYU((8*VME`F)behxVW+?H<)4qWXeIjiU|^6k1>Ce9dA97qWuUD3m{DK=fu;fGi&33c`iKBl(2hYb z7ojrypNSy#zc*azLANx?NxEN`_Ojxo<}6&=XCCA|Zk^-kE=fsQzzMDYq2RN11<#6v z?f1e+&I1qB<7;X4E5+1tYSkRDx1PB7+Zg*M2Fr3ZMe%-WZF56YFPmH!UP&V`^}vRw z8|rJ2gWi5u)U4tWR>NbWtNBFTLb*vQleR6x%c~`h?s(K5Wh!Mo+U1yY`EAs)*10iM z+r+oo=Ls`x)Op*}#F0*02;|~8Rp_S1GBtj8AC=(~BY}p?y&01-j@3*lDl6pj(=Np6 zVHto;z9B-0^b;vv!yKH;C-_`&rW0F8JjLUfxAYsIH}NG_LvU5!DT5A8;=yP!4QRcMIV3tV5Uy4r7a zyqc8tIsc9kS{2*A9?)^`fad+_l9efs?iD&E7c~Ir^Th^PpMIOuns63et$3p22wJlw zcb@(JUf(49z2V6}-(1_hd2AqYd7#)0{yNi~Gg?niW4w7roqKv|AGq0J?ui$qGx zn#ult{gxd>c$cLpcVNmqB)OP7w^W)vCVNykXpca-aA8;5gJCR^b(|TwKk_cEI~cJW zqnzas7v8dIO>f)fnDE6*c~uE+1w-yb=tZvG!fTkzc(52~z8#+09|W@*4eg)pS%qjPerv47mn8AU?(J)$ly(Iim^m?Y|3q(RR>5jKn>*$X zyfC;n?jDca#3VjNfzK294&B3h zOp%n(^Pw{BU8u}Axue12>t5P~iunA#sKfCzO^AEag9s);1!IJmZk&#VD#yF(7jUvS z^)9Nk7m`)c_FI0HlD}ujW)Nipw56rwxV=dC zf;a`uXnIH5j~Fg=)lv+OX#CVk??GRZ!_*tRygC=XMb|QR?K!7wVAA9@QS?i&$F17| z);fH6;j8UJDwDc@lFHz^iZOxr!+a5_U7ISnAVEG~i9Q`*1@3y;0#o);;A=YjH05CX z!=vM)O!wm#O-!o)%5+~GZ#g$`bg#((3Os)ZlbIVGj2;;PTs$xm3!@6-Nu7#)Os>+@EeV~ykiP`)3d{(Rqv>@TJ;=9IR3j39Xp$;RIXt>D)&S1M)SF#Up5f@ip}+i?kyF0a zw&<)!xFWQ0d*SI%%FDb*Ph2YCDt7ng!7-bK^A~vhE2SzCz_d1DW0M|YHIk|9;)rUI zrY|Q@2HJahVYm-I{P82P(h*hr04La9;SjwQG#_}bc)Ij<(hkwMsULSnQs)pG-+RrQ z*I>Zis>9Qw?)^rutwF2TkL>_^G3f))IX z^L?4`BOTgcaQ3CLl;QwT9#W@pj|x1UWe=Tj)M325apRy%a`KmDgc@A8sWpQ9d7~LU za)0v|Nj-hxN;22NWoG#4LB+~TKwyF|{$7!bzpONXf2Vghzeoso-_Fdu-c+f&VKNML z@#xT~rj&|z&7skqaz{<%ZXNy{J-BxO2QGkEjVd*!eg4L0HYQDkJmju13ExxS*pI$w zkn?Mjdmbowq7VI!^}k=XH&z;bzw}GL zlmoadN9`qtX3ivdCfH@a`@&?{)SsX-(hx8YVFQeeynR5uSQ|NIAm4g6WBbbCWYoRK zshSLI{yMLvmcY&XnN_ocWe!lg0Lr_HStVlt($0wn(HzEvRi6VHQ{LDQXyueu`}isf z&jBWWI+mZu-rn8~%vK7^KuZ93l;vb(WVQ?duANrIaKTw0^~);O_CuM%R>Hg=_TIj_ zRn*}s#pew}PL<|n&pCMD%Y1~~wy$(NP{zvVTm}4p7+vp>=ijXE#HhQ(w@wO~!m=}W z+9vkS70kg?g!Sqi$mgTHqU_`;f-{>b z*4Nixf4XXsBS}k3&#a2Jz$uenw`Bo%wSd{^TJkllGAFqWpyM_2%APGf%Y|taZCR=jZ`9Rwc~-~A4wRR?X{8`z z0cW}7LMAS-|M}&gVB^phRHNf7ieoCla#DL2z0iyz)$HuaIqNpsoeq(Iu6ab+09m8l z6Q5kEPGdXalc2}s3UL*XWHw}L`}Nyh_z~p}xQYmRdVHN*$jWN7`PbFhwAvMN1ZecY z9`_Vn)V&9&ZX&x&c@kyL4K4pRxp=WcchzoaO#@d?w%jEo0k>Q(MqyQ6Lctd$S?fGa z2!zC#=4NJtwt&l$js_#1%5)Vz{18x!i zDu$0m@EwEQ^b;9{oWi`>Y4^8`!hUI1KFTfL?fp$=a*fq6T32)8XzeHBGP!u6(Wnd- zV9Lyq!Bl_kE^5z zPDT0-HJoAo9(Np6d_XiWP#L}~ie~UzcozR3d++_$)V8&Of`W(z?1%_hKtvG~1f&MV zMi*&P1T26`lP)C$Y^YS}MF@&g1(Yf^fYM8pDmC;LLTDj9>&~Tn-*dlv{JyjQfb$!V zk1NTVbIeiR_Z?$?c8%K|K5z`Q`86v%n=wLocfKiKwl%1j>Uh{jpWGveTAiO&bhvQ- z(cZ5(+S3GOI^EXY^@wst>~62oS5W8ceQZB(zsM9ie+flb`1G#az0z+bTA_@bYfH$k zNH+NC!-#P5WuaB5;;Z)dzmuzmqRJ)4!rSs-NaF`?@%YRP*L0a3T9zopikt3<${%iE zV)j_YS;+inshQ-p4OPdf#<$z6zb1QC)8u(T;r1w}=T$PG#{kBV%xW$Zpi8yp&E^AS zf(f`m&>h_bd4>V|1TCGIssNaxp$2Z`TJReTgZQduFD31nwR{G+xFeh!{DJ3`2an~z z9nmf;csl^#ezro0!5xn%VilUHVL$8jj`p=tX3tyoQjEmYGfQv32yK~u`$#yK4_8SL z^E>VErfM6zHFGUc{7t+}JsEmjMpLrfQ9f=k%n~0*CC)nQNx4rTR>`=tCoxOGw#=Fy z!Ej;ANZ*b`;T`FM+j`Jv>M@MI>3KfOoq)|NXAwm2Z%`X=JvkG%g9-BSg%oPT{Voa% zx@#6=uPB5Ofb8b6@u=0%(!}}Tyfa58gO`NwLxT0t*&iUSh}?CJPV1URR4;k8%TC!( zyxrwf0nE5kd++mRI)xS^(N+hzx})Fn5DE0Y#A*_qv`4~R$JIFU{FP9 zrbN=4kI->(>oth6QHB(npESmt2*X=>YM+DTRSY|tzvx3LXY4ch+8A4{?#4px;kJtY zey@@ux7KxXB)q~=Qp;MD?_FLGf0oe=hD2cVLuaKj%0b=i*E)3D*pd>q|AC?0PE>E_ z3ke2PYUkhR=P;=?y|T>gB@wyi#xcG=PL-ICpu1A^*~am8t8Bm;Aq1+)%wmKLm>oE&wHQp^iLs^`G= zPG&uQlSG=mlIL#{5*9}&IX-o`b6kF%JB=>OSrfbEoTDta^&{3(an`(4;uAVqz~Dqs zr9m?EvhiUQ9-;W?HzIVB$l@&YoC?%#?8C2xew(m&scTTDc)O;%4gRopLn`01NLL15_4`5g(ROfvdxan8wfi|n8F zS6@N*Gcv3k_A+fOox8QC5)!H35wz`YHp|vHGA4uXdDs3ijFsy@$z9)YukGk1{Nl7k z#;aL7mr-}`-n}_(24FIvz66=(5lYT1ApLd`K{^Xk+4EJFymwQKD4^T8*cyyNa=29! z#tmUr$ZtF-pnIGM7iM6t)LsUV7G0VIYG0Qj02fsg8})%ciPZ#j&sz#4#-ZJM%4b#+ z^iVW0hZCqh?ZS;TrMyS>s8W8`^@FKLl`pnbdob-MF&3d`mqPX?spB!jc^(_@1vlC4T3*$f4Su5FzNBfv{1l!!BlD()3W_>U96fu!Q21JkI4TCpgjF zy0x)gPm;;yHuY_9`nCNdM_8rwR~7G2g=MzY$a&LMfgWM`zR-3D_`l;^Av!W+wrar( z^$%)eHjYkihfb@C^RKjrhZ8RiTG12x7}KOaE0l$y`A#|#=X|Uly86?(ybdL@G;k}N3xDcVcN8(#=#(qQTAW}(HanrAYB-Mb<4NRZv@&4 zJYWxiB&-pJi*(o?3bH`0w-dVG0SXalEd#;KyIR8QZ1jFPXqhQ+RPn~+BfW~e8ire} zFZrL`taY~f9IpOl-ff>~5$nN=f$UR3i!yZKO5ATUpg8i7KHI3W>4{xhT%>YSvm0kK zoXgtJI#mzvlUVcd^A-P5b}{bblg&Tf_!V5j6V9y;+J;}8*OCqlGmF^6Ofv`xl8w+6 z@Njb)D^e&X3fhvgE7ESioIRL)$5XMoekc&C%2+%@%!l~?Vg(v_>o_*LKp8r_2*O*S zK+~+gcDLZ*>8dG|&0_o)EoW*vp6KbPa+&uT!d&;H-+}o5Eb(OvtmOV;cDJOu(2j{& zFV1J4dI>k_0=p9+<##|p;D=uXLLw7bO%w{tK;;%eQ~A|5C@n#lNUmz2QoVf-Z**hu zYQlQMZ^%$4pp!rU029h!GRK7i_~9?CdL~4f2*>)(8=(E;j;)%{_}U-cVD$l7lDneL zeldm}G;dC=KHATwv+2cm&7A?=W-stoSGYd~vIS-_=As&)ZsDYj9}2`52JJ(L&U@HI z(5n4qJ<{zmcI_`O4}ZjwE*tP=KyqQup|q4Q(AN`ef;wvXC`xyl?PL~C-X!c)QeE|# ziDxm}=Ax$d#ZM<(S|!x$7y1w|^7gys4`e;R=LJX@=O}i$y~iDAqHgr>-UTNFq1%4Ut6Q>bAQy5tpI*(T+Z2n)Me#^b)<~Atr zHD8W@Z~|&XH4X)H^}WWA*KgZ3g101><3Pv2cQBK-o&aRutpzWjJmB&{_rU&OW`WK( zprC=??SlJH7%?$1=jN>d7&i}!I_JB0zwLf&6`R{hqyreM4kKyb4e%Gd1<1~sNL;Bt z^mYl#@_N?dH9HU|9=&EX;OKajp+AJmWCpz}bL10ce_HrSYHzc=2y|K$x9+f)P_sw3 zEur%XnV)EHbtcZ_vQC-wgSMqlXE+s9?r6IW#}~^QYRi+d zgNX8OZKt`2vLlXIqOC6_CC0(-cQrQq6F>td9q5 zwTvazqM2VnX~K-EjjGqW%l}0I4}B&_coV+i+l=b%>wNc&ER?_$zl!Knk&(<>rW<$v z=S=cYgnXc7^p|VccGaqNkNjddUo17ty}{rM1n9Iom`afO6XYdZpItaKXl!D#(KHsc z`%GYNNNfW06dco7qy;xOSaN870xmUIfz(+E5Ch*Bt#aHI=`>s2`C z-aN(qJeML?mn&|f-m;38`7jT*Xcab`O_B3=t4*=WIqmegQ`WXh(XqFYm2f{ce>-VS zC?7Kj*P_M~;;VdGRx&69I;veJ)Hz|sEO?zENp5T_{*co~He|h|vgU)^Wk1-ziwtXr zB@zqy0qv)Jy#dCt?VnivhpVab;u!f0`M$$=)kjPqtLNi42Z|K7YZ;hV(2+%eU*^Fb zh0-RUaW{p8gi1Ks4v_L;Y?NC!MftuYhT+b)X2p83gu<$zee9GQo_I_Kb&zAPep_$ZfFF-@Sy0cY<_jJ?zvH6gyZwInHFD^( z#LPJb>pKb(`@ipDQ|j=Qn6#f%ZN%v?e0{e&6!D->{S&F(A?FRoL4;fLqPcXB{EAjE zg=!w?l;Z}~qlv1GOhc7}6lxlol zdmTih`5YumU4Zqrija+Iz5ia8jU`}kY{gD(0Mi-(gax>si%Xb$&j9qE-wLXNZjgQg z=x6ZhV3eM!H&cZ?wA!(Be&CJ1u;Qxd&s3WY_?7TU-;8}V2N(qP-Fe+Q0(q>5Nq#)O zk+<0Eas}Q(wEb>=ULgU)br@XSj&c-c@}DH(-n(XVHAnL*EXlR_yoyC`fKb9}RGVEH zsKh1oG!1k78PH|^B0$nuCu3fPGQ84Y4K+eq`gUKlNFK_A1X`AWc=f6O{iAgliB=yN z6$bLb+2uxI3@gP~m8$!4#e`h7my)Y*b(^6sj@UQ1(fRABFyVIT1NMMz+^L7z-ew>q z_CLI`;h_cVR=e7{XtqYa{rW9+lH)*LEb6EDk3#vX)}aIQDiaz*9^{+C${?8R&NXgT zWCD>yAY9VIouQ`a5Nd2Z^4;HBfIg4BL7>T#Jq{$-01kqq4aiT`rpGBjgk1|p2R=Nn z$l^N80OFO$sKWjsAR`A&j{x>Yc!ytP1})R2OI<&DP`G;Mr1Y8QVx}`emi~hLYO4J= zpzk6<^sc?I#{8vmFJ#YK3{vfD+ znYaZMly74~7$%>so+6(@AutdR^zcq74_KylCtO9&4L-tG_IC-#8^8ZX2cuPkF0uWV zR(AdWq?H=)7&cpkkD3;~-uJx#J`A#@5|}fQhg0>VEV;Na#Gaw{LiJ?-2v5b;=g*n9*fw{=;tx-LG>x9}Hf>gWXnA!~y>#mJQ91$lD zpKgfiWV53Ld@87U$hg^t7$l?Ya~6nU{^2$MgsK(cUOO^ro=_U37+A4bjV~3Q$9d2y zT92?=(V(Xr))VyE$j<|58Hd~qs`MfgOx7`Nb_2?@oIZSt00~o6Gzs+N&Op!!dKgDQ zZ-fMxG>m&t{QUCe%gQ?`coHq5EeZrZhE3H)CP)K`bmO%+L^)6diy^$RN*7M4?wK+Q zkNKr~^zkp+k+;?S8N`?2Uv#i{^}PevM{0QhjWokBklu(jxJ{^&lwi7C~@N-@;r3v;6>&ynVZ@P0Ait>(om5M{@v(`r)hqlpK2b zC>TP`Jx}t1$6^2~=sb|0gORcT9X%Kl7B-+|VPO$X@bm`L+BzB=8WtE}rk1Cwl9Iqg z{NIs>M)QNmwT?^y4PS*H%%y^R0g6_$vc#3FK)@)#6F{M7eOfl&bP`mzgElc)P} ziWJ8b%qi*;VFz1^a5=qrD{0MnQD)h(;67X6@&{foyFp$!qR^3gU{V10e+GnioP#)g zGIyy_i24+O_pj711KvD07cocSBg0StP+${)7za25;~9(tNbEt%FNt7LCS=f*NwE>3 z%kmdl$k3U%m2-!qc! zcPD_Ay64d5e-!Y%HW=4{fOo#4g1%URn)&`#y|KPWD(Y%~vkeFZuR)!B?7ot;bmy)( zRN@2>bBTg!(>%R>edc`6CQ#r!CG`-mTmVHg?)qoT6fSd)9hX+jwJ;5<&i>X8Cjur| zafjI*CNcYVpWDdhunPi{ENU@9CR{rIq|GRB=WP2^dz~Tvj0!8r2Ta*`#{&J#)TX8v zGm0QJm&)^}cHKLx2>zx@fgb*K;OCq6-!WiI@mp!eLzHlcLiqdy0dS?hi(cnEq2M=C zs?5yLbCjxB%DUs%L#|Y<7bpjfTm_7UE1uWh~y#su;@GPa?m}W z9mJ8P2KgflF+m74HZOy5fPKH4U!8%H57e0>z)<7d8S)3VB(=6#4F2?fYr`+_K4)OBUTk#g4kZP za=-Z&@M#pdb$?deKx-ua*?$!E);!ZpomnCXy<;EQ&kZ1XFgEHO%LXhP==oW#^q?Na ze;bx+1tK}myf7$MCI&!r`x@aywIf-H4|W)ZmKGM=fCdG|n})g!4V8!DG|oD0HGPod9eoPoRFx8X||;1@@OyD)r>sGOc%To~AQ zILRd?9fU_TAtE=VhM};lTMBg38--B}TDbp0nltY(Lym|LnyXExUde;54VdWTZXC8^ zLY@vUj`!lyH(hK$fe8?GSK`NyE)PzrhzmoMo(9EeFdZre? zp5juFvbZpie2&E{sJQlxMl1JN<2wJ{T@cQO1>sC#ieBwOYn_zLnZra)0CZ0TP=>kJ zv-W^78+p1G&`i%V3XuQ8%-KQCS4XLh(wK8J^V}Lrirobn%#eo*{>l^fCZC;5kB4lb zYvBW#c-%g&iyJw=#jr11j%{pQE)KIzYr8GKgr$`K?EIJ!r-R}8M~c7493R6gg0kWi z1LdpcbU?2j33Utn4DSjW^{4`_Woc~X`R3fg?q+8MesqV+N6enhMYnh2T9|>`Hl|ix z^1Z^%K2}&l0uB3d%fzt{=MDa|^i9pWhGQXu(4X<}D%qhc2^X^Dyh)S++N{4ZTZLbO zekJK?(7$S)8yiX^BQXzwf(Nfk`w?D=Jo*+VGcH}aOxKUcAdh{lx4MWdrnTzuI#-jj z2HWTRdCA>xH`HBe(cyfN&F1h~?NHj*i;iE752C!5vFjw*A$Q_(ALQq)UYj7-eS~x_v77BUWN!8&_S*N*Za-Alem3)z zeexp+Ny$-4+%_nb>xeI>{<;azshFA%nx%2V*ZR@1wQB=--tL7N-!ny(hUn4rLlwDL zDR-{ge!}j$vYm^fb`qse79WRs4jwHT?2yM^v3`iTFzFr3XJS<~&2MR8f+qGX@pQzA zRsHIWtl`v2jr$xbHM_NpO*hOckPEr{kn)W>aX+akOxN^wCDJnIL(BM^m1lY^mu&s2 zDyi+xmnoPXf)Hx$3NQ+${LbPLrA~iF#LGdeq!)!aYA~jJUHvnHXTzt824Tk|YG+de zj%~ikEZX2gbz^9SA|TJ`zLS-{2u6~f*X2R`AFX^ByKYFlib;rzRH+z#?RMhQpcA?{L7c&knulP6Zv^6tvWLpi}FX6>{ zD~6~Rht#~YRI4|7-{iUWQ=o}DO}?94n(|0x+kTaR)r}!)(Za3WDRLu`w}gDxv*LO; zLk$)Y`ruVwBYY~Vrf#qB<}w??#&$?*)?r{O-CsEEG+RaU>boYEP6AWlCHs#Z?24xY zCTT&UY`3!27~z}22%3p};sJkaTNK{&zWB3q+DckA6V!Izb6978iNf{nR%cgJdZppu zGHsZugr&=anO-#tb=t%S0nn_%k{gbbOD%qcn)EGy#}iwgv7ybYULix*(mlFa@|#n} z7&TEtvBIq(D$C@MkkoQ{Ev8^D-Ki*`2EvXc2WVa2H@zXRKZ5_FoXzg@*U~o6e%Y~9 z{^`qT;_(MNuBmUk{m$}b!%0c~!w)Z6oDcnS*lG0n#XAwfTedu?2`FgF{Br2Xq0Dnz z3YsKlRISc?%&)D@O|{K+^~_X4t08vr1~*B4<|w05CR0m>1B_=i#qcO%Iz+s4*?DQ7 z9RfbE^C&mHfNVEGKwL*rA*3cIVl>xtLNIS=7*cD687_GaMaA?$?=#el7UvILt1cRP zK)6}BWvItKwRkK!8Fb25({TOa7Zdo|apKsJiD+MTWqOU!4s|&&L;lW@QRrd#^abxt z*!YT}`whqGBR?2S&}}LdKVgikP8y{-9(OlGU}!l^)R%<9g)d^{rHDX6O3lGNODJjE z#dq`j&$`9p7n`A=qzhg{PGc@PL~Yr5+QcojC+7!PwOoRCv!PphB*d9BpO2&VN!-oE+7rJ_guRpn&)qDzt3h>p zvRy9z3upm8w&F&b@3EI3dieFFO>3;q5NOAO4T78PD_+`2?R9dZ$~u2qELl1|ePZlJoo+itN(sHK4ZgUR4t_JNYhRFUw`MX z|Lmr&wj@PC3!LgV{Xbs{%ud|0nbmicV)X?+T{$LFP1x*@H!D1UJm&Ob8T>k9mq-XN zroRizkuXpk%y-JnkmXr9nl*XY3xyr= zB_f^l_t=A50gRbU{?e$jZ^#FqyVVK9T;~ktld^o&DCi||quhP&F~g$h-j6z? z9OD=SC$zEXY|^g(%b>u{U*q6_9z$=>N+^l`y-0t4BCvlaY$J9V9~8FyZwC6;Cv3X} zZ@sYQ*8+q8upjY)?65U^nkADwTQao~vX1g$WsyEqP<`u=n#cGPMKH=A^E>d$+cs<6SAP;A0ald`I=JU9O0=qoTN zuoUD$_uWop9=mh$;hVsD&-i20ofEhFaWzn;<6PP~ zFeF9b%)C{F^WEM0;u}JD->Y-e0@xKl}gh%7Ld!e!ja={J_Ws^bzkT;3`{X zG9E<#C^UC0Hu`+GIdUB2p$F&6?vsyBc|F{4=jdNci;$BKdG+T6v1XQI(IwnBnhOJZ zIxm}WUrCYQpmX0@C#$itY)Ln|X8+a@n)hMwlHtpQwD~{(uG8il(s<6=;*|w6_5n4@@sAP4B*=d(%Qp-8yW`U*C)U zVcvRw=O0tm8g2OD_-41y`O{m^eyEcDAk>bz{oraqO(vQTR4zV@0biKzPS z2)00dgcQiJQC^4tYzsSP#ZS+xrP*;hp3dM%>>NqF9~(mmE* z1N99YmzkAt#W4Kre|*=!7B4lUCUawG%HE>dH@ki>*mQTjhcw4@=3g5H9jq#Tv+vKV z*7hELI6;|@{-9dTV8Gm9{s-e^`g4xWf7wUH)<3(U05!cpeXav4C+|H`5dFPiZCSzi zyPMne|Jo=>!N*?V)W25k|NHCXHQR$(GH9!8qu0wy> z2T@YwU#t2*%tVFrvf#b7kEz?Iqkk{bnGDIisKckPaQ?M9Gqbr4*i7W+z!|f!;5^+r7!Jw5S93%Zk8=KRf7N!aMsvP`mZxmBrpvzF^~ykDG2eV2)d#?zOx=&zaU$5XTW8Z zlLgejd!14A!C-I;1Ln)<_UtH+)Ty!xOOCkJtWWBPAqR-?QZ_6EH4d=wKu-iTni zyjEj?0aC!%-VVMELd&UGDJ#oOlLj#W1sqqvdh0G1=Oc5!(1(eZZ~SR<^(nrK{n*1- zO4^l1mcz!aAd*$<#0)V=(DP^0ymZJ$`Cp#t>uF0g($cM6_7X|#FaeAuGaa#qFE07{ ztQbFW+9@Boa7WnO!fV*WrWixYb&l!#$SsN;;BGFS;A=Ij;u zx37>rw|@Ipj6)_ok^U7vA5(p!!jtg+c?G~%G@xYtegVfKeHX0nNZ|qrT+gBU=_lAh zB{|Pplod8WT)rIOjVT^!Uid>Y;M8`tqwD z$^P4_u?61R3cGGke;jksE4UBCBGo_Rv;65&_>>CoajT<>LzNuvjz4%m z0lB#%GMX4(I`AN;7w_iJeQOZYgF7JhNx)Bw31v!OkT09=mXt- zm7|&}Q3WE%(`MOk-fNHsn2QdG(1IX4$3LaN~zD#w7Pvs>sM9B1=t*T zH_LeOFTDJNc@@EyuWd93)Nf_yz<@ZqV#cr+t_r9!5M>=+`6f3#jFuyUl;0PJ!*=bp zjDm;|!5*FV^B#cI>%R)Ee>y_aZ9Y^0(ej}KmyDi!w*3y2_r4<+A5FEy)CY12{q$1u zomCe$12B75J#x_vtr~t>D2WNaLR^)Ur56)Z1(x$><)o+-fA}k$Q7b@J;Y8;Z2B=P* z6dGg&KgXUjH}1Gw(50(X>`$)c_5bu`eZamI@@K80!Tl~)j7w>ZE5H4GtHAB@T9k)8 z;d7%9Uc2}(#R_BB?*0TiD62BR@;njW`F`c2ol4F*`a)sr2|@So=j&gJrQFy=Crp9t zxlcng5CeVW{Ix3O+4H<7b}8CU4@g`|GG)u&nb$9w zHnFNQ)uBXGp7WrlFy2S$o={(dsn)5Fr8<&0)_Q3$kcyR9a49{>=*+q$8S{I!qWk0w zzJfeVPs)G25UrDad0TVQ(~Y1fW| z$f)q1EZH*@SWMGu=hT8E`r?kre9|sHyJp3jvWjgjd+$fVs%jzntQO^QK!4RUHMChW?Oi>nVKE$3~S!m#HFx(N0D_?rXlu60VvI z=2NFxY@AXTrd`9E%sG0Fc+bSBs9w2w9kcc+x24C#>ZSt#TLp7ODZ->4SHC~RYy6h8 zr`V9B9IIiP>e-{gGMrxix<+B(b2YliGxSBUtQ{YmGXIK;*{#uPatdw@9L zN^r$6!O6f_Joy2SDrK~nD6{$W6ZV-4I~iDpk>5%bddTd;xEq>o@fu3W-ps0?Zw1Mg z&DJ?FN`7Kzwowk4Z!x;@l7Fw(M_8;PC1EYx&-_ zgT6~oge@d2zI>W75Iu^+A|S@N7lwY*1z^9iwO;A3tg8LI6`nERNc+ZnIw5Mku-`Um zuP?F63yZ||ZhEo_GO8K@cev~Umr|IU_p~V*nucdvN^7afD%J2PEnM5Q#Qz$7-=~15 z8@|_O{7ksF_VSA>%D0odbgKM;ea0GOeUne#>UvW;5o1uc(sD95$+^0$G9?_vm@rTJ zhN109@i6NgluBkV(7mRZ(B0=o1v9)29l$iTDSP?;yHT#$ak^8$&wIcnBOv9$j<%ND zAmg&qpC;tm&z<{2PBZ;jt!u+~j)<8QUnh;E50_S}JW3;pLaWGoM%7Etq_klk#onm$@la?wD=N6Tv%??~F)8$Ap`f*eG;Fo@j?cpZ0!K}%RpL;> zg%;QwyS@y|DtMwhlN2K2(Sin4VKn(ZOIz0Z3`wU7t1$Cs9A-DlS|5SS0MPdds?beg zHdBOC3ZjgBedYwztTLi%Uw5c4y{kk4J?Q{0^T}_q5XBZudiAjVa57-(k%pQIk0lHm zncOs5DC*LUT1m?0X%2|wn(B|kN7>!T(~vIH#ePz*D8NO-)5~+`=uyrpKmp>EY_|To z?F4opdInr(RS$fGIZH)mqhVq7-LjVsO7nlETvj6+pKw6^Eb!6xD%Wz7j^msAoEy$R zhJ+cTT=j6|m-bsUUU_)+kOlOxw;Fk2+)phjd-7J){d=m^WR3kyoDgNd zf$VlgirlZxXroOLMBn<^9sohJ`f~?yMa6DDf`@y!u6UhjCv%7pu#x8;rxi(1Y>yvA z$KhNw`as^|kt^OKQ$<5CyAZ{eiN*Pa%FZorDXVW>E1qWZ=MGt}3@?Jn`14E~o?G$T z_TQPeQ}>+q^=C85)#~U)8%^JQ-W_dMp`r|b&BfwmuaUCixgcPZ1<4uj4K(*Y%3z9a zLo9sy+Om*kxr?U{)2D|FQ(kEjkDF_fIZVD#YQwopd&c`U&$eVu#jk#h31O?5l$?R{ zrEYrZkq#d$cct6a*e*`!it@}|U3w~}{8qSEytFw)%==h(+}%vMjzV#dtKdZRce94h zfx%*}eTLo!>v0KnumP6LzwX#U1fkG8`LLqV(v%d+yKG3aj+!k}sFB{C^Zx54#rrmN zOVQ*E>G(&{Hh-t85O8B+Xg^m4=?Y68Fd@#^IhWy!sYR(Cy6LxEzi~Lon=PGu*SFd5 z%v^nU&g5Ye(&p@vef-jEj^XazC3H=d*;iyvMzU7u=XG z?F0oVEQ}(AZ>-w(=1i*VDLw;SoLrS0$@dRYgF|sMNH*@}Uq`NY`OPHhvKfSy5M~~) zZ(*%6@5NR+LQZ8!9QO~IfihZ$?8Y?d`C{O6xVPx3kh)z(97`jAl<6(< zWs5QtbvM0a4&#_N5r%~YI=?H`?Z_LoknhN9$~c@Um<4sHe0O{Wtt;Aox3lHYZBA`_ zF#Qr_1<8gZ)DEvaw^yW5MB(0yuJgXJKUnYFMXs7-t$WrZ+28UX|EWg>BGhn=!i#!h z6s(6vGQO5-*w{g}o20vIDxOgWi4F076h6VO!w{Pe)`UkpLF33G={hfKm2QWi_ZxV= z%fv`N@hLR)=y|Qagh)O)OqT^1394Un6E{nyoK^wS`1idr;wGKy*_JeJ*6Kqd=~kM7 zOvf%Z7|DA*N6m@cIYH^d5=`vNCbz~Y>*V=l^SA=L)YiH|;s(W}X zo=KgLiSww5PgoRaf2wcyTcqA+zn4Db4_s@%;%n{IWMa9DEY)4JbQEDQDfT5BrI&dB z0~#&}x~9b9AuPnehCRtB=Y)(@49FqI9pnOTO_?t_s;=_5;c49b2iz#eN2fZxUE%KZ zh#%J*A{DXnI=sj;0cYp#$n+)8o$Khf|LZL0eFfPNiwIRW1++$U)b?oC;eA8#wJ4-j z8LxEwX)VgJHysk+61Wvx8Mf1bKC>7YXH1)v(LrsBPx0tAjT&%GugKRM#8r1U0(X)R z1Y7=H>xIb6d41u_aOo@y5|0$@=Yl=88e++YLMQWcylylF^khHLEzv273`{U;mN;T6 zt+a7ZRPj`TKEiU5sw=p!+#3f%_mo;ct)^W$%C%}d-mYsd13mKDK#g9Q1-3CfVL`N2 z-MP5Oc&hVQf{KW!mX0tOU6ZR*IOJygrb(MOAq6T#Gac*|VjZFrwzELFRyI^^MpHsz z13I(SjT8gB(PQ2Xc5?v_*jy~F5SO|Bsbj}me}i+VewGp^RHgvJhG~VT zqxaDdWZiT^W}LEn*VfZxj#X>B8Fmk%2Awg;WZ%=g&y)`qZo1y<+N?8dxr@ziI6Yfr zs3RzU{d%r2n(JXKTW0UJ`#2{_PDr7YcGiRxbl<;Eh%uv>K{o3d%yWv8M`tKciRYqP zo|Ln2)Jqqbm4QYE6Gg)L1L-4!a>5t0M=?yZ_sKoJHPh;9NM!Z{x;F3XOFh4L46OUv zrlh;{vyvd`r<-Z*n|w64BSP-&zekfDhfc@C@nn5qnm!tmo|{h3_eU?C#tDjL`uzMV z)Jvw3h95(HmD93Oy*gOy59=T9tPXo`vM%PmhTqE&Gi=?%AnmL--6wzRhKgb3WTaLL zsW?aa>JplGdH0DD?12bW>x<#+BHkkcAD^`?^!~zVugDMI;)F(-*^ z4t1o7yNB-8;j=|1v4 z)~ZDQPX>yoiY*-DY4wHr{?JL65V;?=SC6Eh6$pFrb31g9YI5tg4}>uka?o$)jU$Yi?#BNf@ zENMQJMe81^dJLV+uPm6@#mV2CL>$eTfcX)LbpQhN-s0kB4O*G!`#y#qcn0de z9m^cJ{X29J6t|tEV5_2P-NpIulI!@YA6`yW_gzB2W;#FmDxkQiB(tSYsZwOCW)NCe z)Sx+Idxpr6UiIt#Q`aTSXW#X<)hFa0ee02NojBQ*Rj3X^?B!|`Y+#5sid=ykUVha4 zaX#Tjip|`%ZaodIw_FHHZSIeiW?MKbk1iA`ijzo+Q0%eWGz(1geZH-=6V587%8J@z z(t_$(j_i{#E4r6FVcYxkL4=Ec|NXMfwJ3y11;!DUm=t`_2DOpXwDQBQLA&oeA?l&m zq?_|L*-I+IAElMlgl>}}EfEQIBKF@+*dLPUPnq16N}-F3-LI|lfGx{s_+{p%r@Yit zq3Ahh>I$Bw`95V}jlDOvCSBG{Z)#rHLXr*vyEp26RIfS7-6!RP+v5xW0}B1UYc*|K z&&smtuz4_+8{=y4=94foDv#3B^SEBdbQ^+W@-Z`ES7p~iR*(ORiVbqE$Eg-FJZhbT zv^lY&eJ3Sy^fdczBM=yOr<7o0%?M(JTMV_{r@LKTd3-GTH&it!%Wu4d0kvhxVisIN z)fc9aKJwo`K&VBPeo|`PD$;dzE?TAgWOwy8*G-(pWtT8}^Sy*-yPGr6AmDogNO45* zF5N+p;L*H?+QZhCwMoG<8>JH0Oq9t5KTn9%H+7&l?(X6~M#jWNTeaElm#J2*4=QM* zW9xTp1!|$xcLvTh)?B_k8&rKkFuU2!{ReXx~r@w)}KL!+T55pSOZE zy0-M7;({FMK_wrnB6Yd=Yprx-tg4Rje)!6x^zja!*sTw*e?r4FxYPF(7QsV&)Tm^( z4zG1cXlVLi=U1C?dz zeB%J6R;bE&Z}KPl?b?>JqHF}Rt4KhLPd_(xw{;I!tY{6E{Dvx7c*1}a;#SW2bavy9 zT_qkTsf!E%LV4jIfdFWU!`gax@%k@4nHozMVAz?h>1s_G&qT~s=BSx0VHRT?H7oMN z=ZR#YdpOB5y#laRNDyh3p!Pi9yacw)(xF`Qn{mElSV=yva z4eF9u9&i`4V_#Ri(-Ka#z<);${64yFOe%lo za&hf98{Pb5Xm-HRsjZ4bGue}6lYU!!(u(C%47+8 z9q#vjfodRSicr1fm=Vcq0KJ*|KUT_Ns)Kzx7eePao zv?t=#C={X2BhJN*XSNKRfqmo6+m$IY}^uW` z%QIW5El)uKGQoh+ieYGBf)a{`4<*aYH_nxI>6YFn7fQIte%A%C7 z7FI@&1kL|7pf;`KV$VN;DyP5g){_v9CPngI4)<=m(}J4c%a|EUuGd_NfPDvsa0CHQyKBgPEFnw7^O%l;foPy0{*r~22_f1qsxy(Pa#v$>bY9md%a0ov^vU#`0}V`qtG>$N}<`LWuB&V7_55uc|Rlh zRl7W{GR8xMk!Q=F;f;uSx z7SQ$)!g_iXwN^mglrofB1g+P3}etn5fpnSym+gsADI z%R>;bHYs@&sPp@_1b~bV$C#GqBl?poT9MnNxBFLx&25V?eU}qyBeSbb%HqqzWCI1j zB?SK>Eiih3CaC$=7I7_RKad}P5iSWV(cp=j3d?Z?b2Ru`V^1Zy3d5|1*!Jq1=qwgK z`9kO1p_w-|m@1LJ!P~*7(*1;{`5r@olD>VRzcBbf7=xZf`Y78Rp!j$fXjmWqTMN*n zF4Q^yvg;%T^Q*oXAJjsY1%=ggQH$-4C`6OJ#P zIayV0F{2pdQ^RFZ zN&s$+xH?RR0ZymHh?5)hVVShYfcmCegevk#6zk>^qG~YxuZ=qQaTL)wQ)ezQ$$-!qhq*%YLDBtrmkW4zF~qvOe*{|^E{YZM-1U5-8;C63 zV&fh5z%=|LxEoWLd9S4%|KzYQCzQO&(zX!>zh1$LZjTVaFrzwA*$!&zd`!JxiQNNP1_W;Hpe;+aHVhqw<;|A8=v13 z#Luuxq)7zp$MAtdr5;Yu;7X+BglJ#0JHf~<;qE3A3ztjw-E|oWV5NynwiEKwBdv>O-DPy}$3=Sy_k%@`MT7SOTqW zW6$R(^D2P;OU-Kueso^4LOAVSP5H}duCVZX7&AD$LAb~xXI>nhH_Rc4>p`)iO5owL!eWu+%lBEokSto_4O;<|%W3E9Pg(+%%ipByNVT8hsArmhk_v)tZ zQ+8;iXPMu5q$d3h-q1jo1X3zt)8K5@^QeTq^HtQ9O(~WIq&xY}OK5uo)E&)hX!%)M z((Miv=bt@6vtSfUp~d3cs*GE7u9Zr0yO*2)$ja8j1a?v1I-E5W2)P^Wx@tRz{n$5b zxpMRVWnsG-BclPkbA)}28Ch;{BYkhF?tsT*%*TYAkpJ1*+p-rF1-7!d?CpB~#w_Y@ zfn@m^Ou_HtPc?~=kbgs5z;C2v)(73&GfN@G4o1stBX zs(IL}kv64hpeNDuk@WD9L&6mAO#{~|*WvUNBrv*QEtP9;g5zikxzf&lbsMYQLyI|Y zgM0#QIEw$DI%R)+t(yIn}iNK_U*=Pwmb>pp;$cNM@M-W9VtA_i;~w zm$)n*Z)-c_fb~fEQY?zKI@Jvgw*ar&=R+|WlvSZ%lg|4(f6@g2lwSd%SqyD4sjL(O zC>WUPZbJJs3mE!z`2!De(hhY`3LIWL6lvsP4@;(Aw|71}Ruk{N`9!nr-Psc`QAhGE6`gc4)KfdM`er1 z=O`XAgks+uk$HA^dLpDFA8n8^K`Qr+3=-wbvgOMZ7E6w-25O!X+l#s?_(pT|PN?~L zWPi8Trbt_bprU8aeiH&wiu#6wx!@Re+$4qV>UUm<(o6rUj#P7#s<_4Q|J#4};de7K zcFv6Tt0wyaU@Q!|+AhxrXV&Hh`D^7rTvyXG^sB^7n6lop(}9ulVe9%#?@Swo$3qnE z2KSS3@j=cur4SKv{D?}pCtbLWkv;A!IT zM=ZUM=8+xjan8v0WExRARBOOkGo9c~u(vWV!C8tr9+T`*FYR0R*ZUJlau-&)CFfm9 zXZ)g(`t|6iV7-HtB=I^{mq^tn^%OV{@|*mN1uTgjV)x`%%WEv zNiMbjrGYHJ&!a@aewKNs>R6d*T;12xPKLN~gI~ugoxTI-rZF9H{$=rgwSO zqfAw?;KR!Ja+x0SL2dzA{tR~atrF_K4C2Rt1o}R5K~#Wy9LG6E*V-#Sd$O;pWCiq( zqaKlBfll@{9qO3rDa9AEq$kswJhq0wB{Mh_q3Tk=bG>A#!B5zLeb`9c>Z*NN%JGe? z1M9-VzQQD_u=LeTLW10=Gf}v0Do!fM{G6tFyPYndU|oLK77hM}Z9T1(K(soSN%Qr~wZsKyo#nXVAHyrtigzx}Amc|CdQ1`~6YZNZ}_lk)W)8ZUP@HEVKq%{P9Q zYX2ISc{hw|DCre(Rw6oIuGP_f)n~W6;huWxL2m83z7MLeb}P@e-l|=osoE6?^gldn z?@?hl=n9)FPK#c!xsmCgSzx);?FwHt{fnv44UHjf!kp%z0omBPpI@tM$~mjO9=9v( z-_K_w|Ao+VAWbM_)rf{fC4-daVbA$C2PceVQ6A5pzS;ZX?8AWWC9i1Ku8LOzmU>sz z5Ae-p`1AQ4G_LJ&mh1NTaCJ&GWA)BACrvy*7`~F@Cq{Gg5ns>PD)sur_{-&^WJBcoHoGmr%KmNSy(3SbT?n0XnnPn~tFQC|lOE5Z6Fohb}LQyp&@qNuo62+Of zU8FbAIKo)wWf4OoZom-sXQsOV|Iz#`yQuy-#{XY4hWbgql1*2Qf#$`^WmRZRZ3i(` zcd9L9Yr=R-D2sIy*@U$oP;KF-yNRe* zM{@>;B=!ZmqU?x}(~WQM+&X*iI;v>o(r21E(EQ_9(CSY=-(MTMX2P~6`otRT6kSb* zvQCl9^vww|$Dl0p22tIY9o6_mh8%q47mVK2cCl(gnHl`s?okey1kc^&eF9}bv-vRZ zYLqS~ML9^2T;E2kI5}ySGMikH35($zWmD`VUigm@GsT@d`#uYOy}3p-G;^zp!_xyt zB7y4RzppjDw7Tc)&5KcU;D=7jf08hvA<8kAos6rDY`tOa0xel9GN@NRMCd=cs@a@1;t z|GJEw%EVo>EqWD8Z2V2K`%d_MM{7 zC#&^(-D(SV%YKw-J?p|9JFDqhAt~nGuVhkaLV8+izhjZE3G)Y6a{7wzmxi<0!r*RD1N0iB+K|-~M zO#aT!4#$Tm$K262a<{kcj}2i*fyx`Pvk=KBFGRVYaJ44d7`FL|AaT^-H)rWwpq<;pSK{Ut|aCt{KG$qiA58<~0KP>!$!wS(8->&yh&mE55 zD0%q%o4MQaHavZv2@9*m;WsUkr^5r@tdGvL19lcU0X(X;12o-zOQ2Mh^5U zE_OiB1K5sENH%dK_TsK@#QX{)frc-fe2a{q{>D|e zE)E`0JzxC$JODAo^`rCR$GGur5AA z?^%iv@Xv-9(>9y@HmUM7EjFJuRP1+R|75qJZRBFNf5WzyTMvi0t;Hu9&R<8y4QUJ0YioR(+8E{i-`V)*ZU=<1zpC;Qmh^v);Az6X~!=ntX@La#ygZ zLH<_PiL=XEm!nv1oRpHi8*T}9l3rsJgSU*sNdG(6*f({DpG@!a>VusR$R$6m7XhO9T{d7^j+`WF7w`%ZyjVALc?!tn zpr&kk-UW@vi(8bu;KD2*N^*R$FUk_CZ{w<5bCQt=TTel>JAYvQ6VpVlD;yog~`wJ%UU?%N6 z8moH?V|APySDO|l;iAUnE$=RFPkW`7qaOOgjNzef!m;z`maT&2m+Ag&!20ccxla6( zN7lS&?ltA}hO75~T^Q0MR)rH4Sr>uDFfTc^T!Kyo3XCCApf3YbZybRyZRhonk^p`s zZv)5HtS@+st{_*0X>gnyJ}-cWyy)p|dCHZ-(){!~Jd094=V17$xdYhw$U=E$;cY{W!h9-kcwI@4IpR z=NCUL_b<->hhzESSWqncVH!V7;|F5-%N_WiF%99FxD~&10k$3a;i7-I=>M5}_~Y&^ z>JU(t_apE4k$3#482t4P{4kBbHn!k9|2I?eBZOrkhAyVowTgml97b-|<3J`$|6-BT z>g_($7=N(q;V<50J{+?JT+M5@jg^Ucq@y(%{^S59Q6b+DP)w1T%_HuKq`Vbe_mU%g zhOZ=_m%n+jxhW{X>9a4$(%Q^BSTZqhIx!A>N`ezm7@BJqm?~&0WfRPC3ng)GL@g0( z81ZFC;$`KsJ&vO;4Fy;FuG(IdLd@@R7}PEXN!H8bFev*B%8Sz8puNnd6e7AuvfXWR zLCysx>0DE6UnTdxn|Ayg8Qhl)l)2!OpdRZ?tuiuf0V)oRWx)F~(zhpR4rXT3XWI!T zg9}6OF9q<~Z7zex&}`lF)6@RoDltMKOi#4cOm?SvOea-c6oL;`M(vGS5OXjPLM(DJ z(7BeRa&C0$m2TJJw*P#_VAg?rUyywzuiMzqJk~OflSovAQmI{hw%U8Fxt)>StElfj zO&YX2eW5k*=^BQqIB9LkGW%lu)b=dCIu#zcr~vM1vNN0AdJX?ZvYC$!-E+OQ*Ef~o*F_4Y z5vF3rP|5mYL-^4#T&>A%FaLxiuW7L6D#v7)0O!mG>Ki|~Sekf^EJJPD`PGwkjY52k zUiFI44A7ISi*v6WF2xO&3K@;v{b0{VMTrt z6g7S%y+KASqX`BJ&A%?@ghBX@m!Q2)hQaz>)gY82lw`-C_Hq;qy;4$7l$n{%1rmV| z0Y(v?+VISS%`?h!2=rm&##fgw;#0kzmVhP^K}e-(cJkMivM-#Fwi3F|CxV&ZM*-J_ zRJMZa+Fn!jdM|y?=nb^>J(tZF%>^C=e!D(tG3yxWkJh{Q{Zcjwgg?L8haLE*TlWk! z?Of2m0F-dd z6XOHc?E=qcY7|dPbu3Eat{{MTQ*pLQD~EdCqYeUISZ~o@=J}Ry&oG>4Cj~>YM5=mG zp`p7{35+>1Qq*`f?I{LlrMUefPD(-k^_7(I{&T3#^bpeE? ztH_p)1zf4y*F3K)L*A99Qh%FN8T+i8}zsGGZb^V?IoyN`#aEnR)J0S&m9 zp&`8DCR*uAUj3&|A+?Qn0sE!k+|c;DC8`=iHKN^2=G2IWzn`E zzGMu|K4DNy@SkYEt7BtO3^V7O7Q9OC0$l||+?%p1;TBq|ydJPJGwEr*J1m+OK^z`G zCH`b9o_(@3Z#H3XBw;4QKvS(SX4cSlHO9BHvsZ0kVqr97Du!3~b3#|RL&S<{`Ws%x zX*Y1fyHj(?-V^x@xc$Pi`wXZc>p`p)$7i=XG?PK*Q9m7~{eE`2XEUD}^`W>XrOR;8 zkv=^`f8J)wy1+glX=(@Ik=!`>=z5pa=^zQl22*;r)LGy>I7RGEwraImk0i+rh!&tg zIpj1Hiia>QJUVKpS{ao&j^rt)h<#JJy^*|`Eud!}do<_OwZ>Ny#r+yjTnxPqW*Rk% zES-EVD4WnL>IZ^KelT`vVYjIsmL~{F>{AKOP`sNhVjVc+4o8{e)URaJ)DW%-1{y1Qk5j?dzFZ3!r(3FEi%kzX@62}(ohs^4Pq?2KP&&-?;q82-a(zYdpJB@% z-INYhq!UlD7yDV7qW0y{qYnCG5af~ewXKZd2EHUvhrtPpIts{ypDUm*bwlmwMxsz z+!d=taXObmOZzk*wN`8HzrSPMzIC7WUL0-JB=p*LOsT8awQ7=F*QSk5ey}w=WIym! zJECr~e14w#6zLF+5VbryKw?f&ZtB^3{*~O3rY&ds$}65%nh^SKx{Sxp-3CIe~q$o%XRkc}|)a#)2#2RKf*X zgJNCur*lbj&Gi`{D{f-9GyOizKUP-_a|nrF!-%OW2Qj8VM)>eOb0J1tsu97XQiU{! zzal%ov2_{3L_~#TrSE|2KfYMA6WN*CQ?p4PnnLqe-RApvp!_PT;^$J3DejIml=gIBmZFgMe)tcmk8frZ#kvfOYv@J z_UuW%So9*O$fcqd23BlRY0R zp55K5Yd0k5w#&;7XdWc~b8` zh6x){PrZUvNF|SW8v> zdQ5MJh2yx^u+hY(tVx|Y@yjs<9(xTmfu_wLDnh|P{xI|@>_XHmIHJ)?*(Ch-(o-0& za>!XFl*NeBAYX<8nU^xz^D)UCRQU`Zqmk>>?55;4|4J-3jmDTC#dSMGx4O3Mb1hc5 zMM9%yq%<8ff;x#O_1tz{GO?j6U?_>qJrBxd`!BQ2!2yV$5!mh1YeuWLqH9ecAFbF@ zAJY`7GRE8MuKeJht*fYf%i#{#6QUAVK$ttmg!x$OPg8~!OYySh2Anj1fB*PaEg*sO ze$EKXZ}1(Qe7#2OX$=-O?H1NS*t%zMad>uNp@wsRpXK(O7$~G_TLf`SPbH|i?@oCv z%4^F;Q=qtm0QZOaAa)CE6qZ;g=14hFzTP#lGrtkWeWZoMBY$4W=z8gLMU5`v?7dUd za6`TWRjJdUC+l452`J{R*XCOVotQ8I0R8$5D3${i2kFwK< zWn!1P7R6nmybDV_$RD1Jc3>bRc4DJuan^&}C>ssMV1KaY{@sii11j=84er7CPALyQ zsu;kjQ%J_LJwwJ4M|}anOUC*t` ztk|2;FsGU2<*ma27cp9^LvNB1-E3pvhD2YA0a>mWpjD#<392QB?+4e7b7^p;xeuGi z4~f2nxMNl>(Kw&A!lO6B(a;98+665vED9`o5Q?&S$7%9;e;655C6#`9Qz_oufnkhx zO!~5vXTQF&@p~U58u@uf#y%jND+C~}YL9KEO@S>BjW7|ErfmG`xUH@21TQwuTY+RG zccu=yyn|rlFyH*p-6Q=bMwLr-aeJNN7VsG*tX;3FM3eIG0}k0ujls>uwATAf?y_W4 zKg3>jc!sZf6jB+|@U|=S+S}Q3XI(i^9+)NVf3%I^FFd;Sp+#p`pjzaNB*y`so=DU#+TA}g(so%Y#=H`kasrkYHs-kKa@Sr18FV zqfQ7(X_^b($z86(?ejN95nsM|6)u`pORo4K`cz$# zVFX4eV6$Bac$21$Z=)<;^QRGJ?;TT5lB4GlqTRAXMJih2&~8GKukKIegx5XHEg=4& zrZNAd3wE}v>FVmn8Hg>TS0vgB<2JD+oUu2M3nq_hu*DkR-lLcOoK!-R-{Wa?jjy`m zW4M%BL0-a$G_JV=cdT32%8G?mV@Kx4ZX5NHLFL2DA5M(%M(ieAeS&G(eS-7stX^b? zb%DOOe~#gp$W4qO))f&1nJ{Y=JmAo$Q(8iSJoA8GLXosWTBVkSgyywh>#?YdJI>I2 zqs*8YNephS#i~~~j=WiowB1<+tx`&lxt%ZfbzfSm|U^S$bB10-xu>7s+pyaBkA5VG*WywcxEw0CL)kNd-C-ujSR2t_O4OLh5zlExy6hR6Gk_Sc2I=n~m znSK5@1V{F9Yqrm}xmp=$D=_JxwA^ux*F`=kY23YgH}H!?mbhk)XTKar&~RW@vKaQk zZkUlYtS&ps3!bh5l7G4>ZX*wK^o}B%(-S|Y28hx5ukJ}(AsruIkv`KKZ~N0r5dvmTbM-b`*fB_X#g1$MJs}Mqa;~N z7%011vf)uR`8P4nbn1phVc3>q-(c98!Bvdj!J)|29wk-3a{*E(tR+kFDM3^i4K1&m zuWQ&BZAORj*(Wo6f{{+f6OfcXi~Lx9wH4lzXdcjb^}ZvDggP{4-_X*(!z1wEXml)AWjON+LPmzxtHeP-Cs-1!pX`DP zg&JuzRFo-d;0-pm9(#xcw^QQ*aw(?2 zt7*lehZG!#DoZ%)E20i#ykDJe5vgt-wMm!ZaBh8N@otdVaVy1y1|zV^`7XB(lJb`+ zXFY=D$TH5*j|doU&Jl#cKaJXkh7cL>Sp5X2$f9hE=4`tNGmvP)y(Te7UXd^kXYoF< zgnRauzuwZ%wHKaE6l|e7CdvdWi52b7gUO;Rufy(mh%Y@4(%%)^?{WuZbBJwPy|1bM z+D*(6>PU(jJG8mP4}qS6=!@(o(NS#31gGTU;$k-U;hGngAct!V;qWQDG+H#9ns1~8 z(%gNWiQW<)SNm?I;h-S3@5w{foRSw=87KIS?5^J@{B(D#%hfr@ZMu|Ro4iKa&-Xj< zT5fX6|B=M~W?!ECd%n5pUwt@fqXnbIrhD63TV=bK?+fq1P||PH6J{dXRCZAFi(KQn z6A!V7Q8&T+k~KA_3Uvn=F6+(m-KiP4`HU`v2?krco)4Dqycznz8uYp1XYr$beJ}D` z<>w+2c|M>D#s1u=Bo@+4!!Oqg$pePO0ef8Chz%qz zHtO*3u)3w(&GZ)=39!eY*PNqvSe0Y5yrbufy(zJ_M~bAW!8wKe_qRFcr!MmylP1}= zWMx;TnS`fX+G!qyy*0IZKWPjy?S+F3N^|ReJew}o-iy3+#VDcil!{{K!5@{ z1HD&RY30wOv002^L>k>?^tHoly(&4Iwa&L}9fPAYci>p^xx7bSSlBs}yEr+y$ikVd zG!?Wbv~f&jt`VsZD(bh_n8wy`r=)3Cx-9NHWxn~&WCPIlN@Pd0dVVyLo@6KsP7+G( zvx3@&&X>VxiEKM4E}(jn)2#jYRBTsbQtByVUJhf8q)mR?WHAtVq-QAg(jUOZwH?Y6^kVHQDB*o7k5} zkZtZiG?<(b0GHp@aApvc=TE&Et2CRAos<5^exX>JQ@Ozm55B@~)0aYLThbdmxP{tz z{|0GQ>EX;SkULAB6EDR^;JukBdrV5Bt&3A6X7KsTyfWK{V;!s%^TCWz zrK<}iNJ){S`*gO?Sa-hj)fB8*#W0B;+_(5s`}x~bQ#QEQfm;@es%G~~(h3R+Qj;!8 zmSS!z)3UhVUxNHkBe!8At$ZBtGQSahKsyjkI9GsZ!Re~)^;y-Bi-sflp4RStO4E*s zgC%k24hg8EJY1DAV}&`0O6F8alBIkd=0V#Cvuut8TPVlV$}5l}N8&tJ;7_Oyn>eVM zI)-xa`<6>dcRKKn&%$oi>}EJR>c4=Q@*bocTmkbe(qcJwsJ$BL27jsQ>gmN7K4-#6 zj3%e3MeFmv!rxp_(KHjrvR8u(w%{u7cdbf_IMDO-Eh|Y+!ove|d2O9<&v*LcA`79foSfa?+J>dH&!u!Vcnmkx-e(=R z0{5wkL5IY!LJ#yT5}T6_P{i6lLB}_$ZBTYZooDH!zAsASsJ`tK-}@i;l(n0t`W&n05PGN}_BAi#E3fL|g!AwSV0h;#YGG=wLg+g#(0PDUJpB@NqWggR z@iBsQU&CIh|J9o}VXZXRwkOWvH6 z)f=TaX2iHnr_&WP9SEbv^8qgm>Ge6qL8uEF#iGMhH7>>lsmYTXPZH%>=w8sqr|rCd zpY1DW;O;B?(s$&q)@bW>h5O$kMML?V%l;b9jwj}+3HgT9)ktR|V0y5jVTuz>KpB|; z49x4s;-KWHbMqwN`d?EwEnz?_0HM3nP#P%IL zZQ<6%$D5F_??~40=LrVvC(u-1Lx+~U3}nr=W{sE5Dh*Bn)(RYck92Ex3aA`cxPf-3 z#JT$h)u-s?C{+!{Qlv?4du=m~Z{1*-JE3snYKD=LFl{I)#)WbjdY+=L65y&)^?M>t zswiRLQOddW9cx9aq;)msPb=@bcYk@-&+s8np$|E8s{{Yae4D^IchKEb`Gtlg67B6* zgblddA7!C6OmLJRUfi6Of2*~$26|y%zkc2My27jz8l=hnkv*hr^{V-xpvFMRbl11Q z8rixw`EB$1DJgZmd~i``LFr?s^BbC?lLe4ev_Ms7^F(1q{P4=2GKhrYohjkv6Kz zPr<*tq_&n(g&#*5Yz6egpB(4Hbmf&_-`5*bmSn=Rn04st3?kp>^f065(Rhg zqNs%AxftiFeO@ z#u@XyTUl2i&yqOLvV9UY=xvV_0KLT^n+i09yf{7XOst*r+3Mc~-? zQo)F`s$7)%9TQ!as3)Hu+{57kbu>X99v)3X4^p_AKq=CIi*@+zgX`fyys*#>HlUfO zK_5A{{qR9ISgjFZV{;M;FlYw6Ym_lEYSv_!9RQA9v=rMXpmYGJKdi>>iN3nnyK)EP zV`ub@Nb<^`b&B1n&y!AQ`{^>&s3CZ2NEh@(Q)V>V1Q$nJ{J-@J{9IkecvHq^L00$4 zND%jME#K2?R#s6HHojvriFIh*dSZy(<>gnHeqE@K5X)ARmW8g|>a@+7lw_!BKXG*C za-e1-2HcdBzcici$#ETZ&$3*OGzkI&!5KI81Rx|-5f7@42K9Q-LLmJVYJ8hH2PK2r zbSLHLdlj2V?|>5$07H@OFS2!A)W#}LwS(6{LTrI(QNe&?NR&~{vu(@X4LPt|7hAOz z9vXIGX-;xvW;k-N(r_1FX?S5@a&!fjl(_)ch9gmnK07nUYmkW6JCb>wc=IzQWvkm*X%|`~wJ{}nkW+p6FIgjMOnyl2 zCF-rUiX^#yrIkxvxY?6?*DLr#p0jlXT@<3LPJg*F=mBlmQ>e>GnyLY2ij*u-Aey?^8#Vf^+~e6Z}sL3PB0({*h}3_Ew1y+#hi{FUV0vKmj34tL-(FF zzq&Pw0873cnp>edkB9mTKa^v`R+T+egg_rgXn zF3?!%8c0fuoaj{Qc6`!5-U>TaAl|%47R29 z%WdFUN;d3UKz-X6oWmiCKi$Whsw?fFQLrwth&RXlezW_g)whMTefRp`T#(%NVSZTzFRB(rDAuhUUF~W(}|HM%!VO zyJBv7FuwIT&A>ZVYTxh{K1$R_v963o!+NOEx3K=RE6BmJcpxsJtzqfY3-U^gs|kfr zE;UW-$Q5+WtinKaLT*r)&KgBI-ek}*QZO3npjWpX8!yE(dhx1^K7Yp5`uTmN715Q_ zuiKF3*np!5e98eo)%4eXJkqtzGH&zD?UDTgi5{Y_DF;I)skG;V)zHr5@^9~i9+q-+ z1m&8%#?kV(;kmM&;K?gO_iy6ij^hHej!#bQ*&MhjsC2s7pct-2H3OWQ%h zZ*P+OK?FwEa!Kw4tlE+cezPA5ic}1O(|IyOhzv45cATlmT5R6s-C0V+Kx6xaeP_#I zXvGFHf9P@1x2fQ-fM((T;VZoo6k_qNo7lnjfg;OjHfQCt?-K)-W8nhyVJ3{rtXN|U zG6H94v;_i{!$MWSS(E(vY`+mT3m2&(S}!`k61!gsEu%w%)B$aRZiC$MhYaY0h#i7fa-hDb zlu0Rm=q&M$KFo4-j_6h4o(%kmD$& zUJfzPQg66LWoBGdAdjA~yNmQ_0u~B)Th*V&qfUT@#6 zE>tkK!A|^)2|idKI*k=+jDa+*qC2~_?+r=`Ja7lLVcH-d@BG~4 z#p^-Kzr`li+}yj4dzK!{(i)$%U!ibh^U(`g;(-I@UwS}b>4`p`K5hERKy8o?y)BVL zAk`fRtJU~St*p|rUF(Xm=`_47H6H3ZU5JsIIe4Og#(cIN)SZSlK36zlnpHW$d;u7- zrB7z}jVz}F$yj`CYEl+&T1y{n&IxtcM~MRDCxDg%e{4V0>aFw@Je4QZt#?Yb+NmF| z*qOiiGb^KP)B&h#I~Phnu69r~-<%wK0E5!p6+6be=XXMPZfy8KgspJU$=a$&&SJjQ zX=LXZI*|W@KhwAKfu4Y}B)KKgf$KKfom7Etif&%*v{^zf4rF2j5PP$B_+9%gv%u|s zhX-B=3C@X-IuqBV8+SufVduD>1{Y!|2OxSNNh{n5pP;MexI|56{J0G;sGj3jCen*i zTU5D6QJu2wSQjDyeNGD22Cy`!9&vpObIyA31nzhFOE}p@pfbB8F7S z6f>1ZM@AwiN0TpmrQ@veeSN22y3fx{wSW0?Zmx#e@#3-9hyi&CE|wBGcx6Tevv{lA zp4;sS>=_C5t8$5T{PlJ!$1$zP-_ zo)q$XWD#AWO3rfhjgG1zj@)ys{1RTB7f&0%1d_svSoXX2I*Q&)o9@akNV|0A+~nRt zz?}f8Q2zBYV@F){ZhlFl;71bTBTr$Yg98Jnuv7xlhh}`CAGr4PC!1CIWSkF$k!CJcuV}7mr!4W zyh7@F;E*Z{U9Zd~mo~+wW8+5?lL}f$ZfN2|vD+WL%oZs4i29}5^A6~l6lFb@8d;CcI*}Tax zc?#8xOp|QA7X9oCA?dss3PJ7D(2esHTJA9P+3e@D#M61B6Y7zpt%|Au zdi_6ENP#5Njy$rr@IoGIqKXwk&{pO@fI@3JFrAuqy(R6CZRx>3TtS_Vq_6{w+j z4bqPLGqmXkpbRdB+N3Bq*={k|USF?8EKm6xS{8a{rv*?ctza8^eCD1tH8owZMf4w#MOoovI2Emfk6*hu3|}}#nT3XK0Xw|* zqIuqLAM{tx1(M7PIrrhifBV_L8OefqEnAeL{m1XPdLCVDf~T5M!T*lbO48rj-*4}F z%YxL3RYzSz>f+$~yNxYe|K0!iVd0vuSQv8QqD*BCE{YBR`1yrLv--#J{rUF%IKIET zG(T?NA}901c>i!Ge>S6UiX8vt`_9{k(SPRx{MGCHILiMINBQIUenjE#xZpp(_yJ=6 zinxAYz`sMjKQQ25Pv!>(TmXwa!Lc*XQ{SJKKm-Q;)&+cV9dfNQ*!Y^{JG4LJj?6Y%DP+RVQm6j%E zkgA)t+hC+6Z(l=eYs4T_d85SivPGo!?%nXrBlYyIE(Ljc8{kgnos&Fppc{xlb+H|I z&+4;Ae_tB?cA?c5>Z78l-ug%R=dXAD|HXpPnhi9d^3oPAR_xgXAqLy(+i^~NwwCW= z_vwc_VDu%^w64YxnhS^4x*0aw|H~1c+vX`k5o7nc2<$nTD55_w*8R&n(Vc=nQ`R(L zxM5Cvg2%<+|Dij_Nvu-P)6=uQbm{qiIXS~?mX@j^d-m*!EiElQD^mXMoj33VV*UL5 zYN17W$~re9A|e*5&1bc>wddSW*ZfsMfwV!YVbNiOnVFfms?D;pvaJ5Lz|wqgVl_20 z(^IK+ir1>~6~DpE^mJ^MW|CHHRfM!-PkupxE%4U*CxHypy<)`*n_qtUCGXg?hrFDe z6;ZZ?vCSA4I?RYMjB43GG$hiI(;fP5_+G4xQ{(!3`wx>~n88ka) z)`k5is{ii`=<2sn`|W-D(eKtj|4ZUgRIkJiIWD z_JA`Ohqk2Q?x1%8J2|b)ymMVd+*4>qg<88dc~jlw>i4|cg@0OnT7Yr zs0O|xtS97N2v5+i5y&N?uy-U#)N!OGY;a_xdU|#?o*1%6FPLld=J#vYuI*lZ`q+@B7H8atE+p!IITf;X6D<&)QLy8>Elm2@WraI;_p9eAOGzyJeAR7bv*Fj5AnbL z-I1T5!Bce;qwvHG5DLizXsYGd_Ex*evSDZXc`fFV>$)r>W;M-Aj48uACt+@K?HVHN zb=eruJwvpxwQgtr&C*1kA7R3PVLa7ybu^0DTkc&(c7hkWO6A={&v3wM25_!zEiF%r zw-Ph5v8>Fw(Sd>8GqbZHm3nlAaO~j735#hliqU&@qT}CpNfs_ci9U1-1rsQ|+}t1R z?d`ko+__^z+RXslv?()APk?A;Ab$W)#ya~6P9z(`$Rp7^K%$l%ajS_}ciL{t}LQ0B7G#Lku z!~em9$HqFlHk}%Ga&s&E0v}UUPgnO8G;v}lyfLuv;HKtgC8b2N(HPcpkjlgVJ^U^D z#S9Z@eEclT06%1VrpCx{oHvMeW*R3U5uim?(;kn~oRd0m;DDxFF)-yC+MpZPJG4qL z^x~i2`XYwwNo6~iaphwcc&X-1nl$lOX4phIc-VuQWq7yL*x1<3nv8Tq=Q6w<)8?&P zMTZ};f$Td~R@@?Dkr(^(`!<8_SA)(tv}}U8z(Xa`1(VRjx#nGoZ9Y@M>)+YF;LL9y z+nzE;WOPqbk9Bu~CdXtb&}DmDnmmq~|JxI{)j{JV_$rf9y`oqK| z8(pUxFXQ1*WGrI|47*G+E|C#K#$Ubq;&W~7*)81MB5+68=I7^?kmLUSQ6RTX;Q`ww z)2GG}iQ%%Nqhr24D0Afx4G*h?h{HsRz}8;LYhNA`+xmuwKL({dmTP-;St);La8M}( zI)bM{q#Z{-*gHDL`@@Ud!lTYhO-*e94o>Rt#s#y?mSEw66uhXBsNcVwqLSK?G3?qH zuO|kxzfz`SI2oCzgMEGP%OrMFg@qXz1BgR=e>Ya?jU^`%UXcmFuuM275~qnZUSz|R zMvtW{{FlxD*Hb%}xJ+jZ8Ron4@;dbBbynVZK_PBcxQ)MDhA(VrYTB}#(Z6fc>ewM` z4O)38P`GNLSuGSCs7b@k^~ES^FvL!kVL*I-_UxIPl2X<2W{jB;6M?@)VWFA$XIw7a z3d_vraN$2cT+UDoc-#9#(y}F2D+!BrsrVxH&*4F2k0&z&gr~l!lo?ygCbiOedQ94nI?Mz_|rcj8x=@)f4X$pP35gVhyVhNkX z*ya~qmGj*QJY$7UZ>1^tU@zu~x(r~(Xv~-dVLJBMR-iz>Y;)A1uL(iSNcWnW= z<^eXyOhNOw`P^Jw&9NWx^=-J|Gp)Q!@y_8`WiXbmszFysg7fWkzO~?jy|Gy5qA$Y# za*T)2G0M&LePp?)5E=#itf}(Jirw^#PdEXlx1Cpeqq68?RRQBqdHX6UDQVN`{}brk-X4W^xd)tEw{Bfej|2sl zUHf0hnNy=={a)f;V?)%QkKqkw(}b>6HtBykXMQ0N01z+d&JbEgqj2zrGwp>tt;taM zL{M0PjSI`dC@Iy&3wyyJCeku0v5G`SMniNhu8D4P0WTLN#>@!mI$#|C&zR!n^X0p} zFyr)4tvQotcxNAMXnNukE@_G=u=J89<4nJic^QTnB*xqJn3ds&j)3QAS_)TkC7?DH z6&0$kF5Sce^C>e{cdo*mBC{xI$u$2C9FSc*cvBdYRJW!9qcSX@_{tFX_Y1V>7r~E@ zu+tQR@W5Jz7+J#WiLdX$9ivaIS~3nucC?m^b2T&{gCQxyp}xLdCz8MpM!`EZ&k!N* z!Z2UFCu8YK?!Bf-n3Gr&J!B+eTW|-(g+;cY*6BFcZ5uO5|16R7GqvO0Bm^bC_ojb;l{3c)& zj0OX;;nDeoR}*mWCLAyp1_aVoa4BN{^-}C#{;C5H?qCv5cAQ!2o1Uju*;w5aB9>V-pP* zcN}@(%O#bnK2F##w%PT>A87r*TsI2rp0NUO^+8m^)r1M_Cec~O6kM^1Q`og*M{dzS ze*9=!!b_qz?!0kl4a3#NrMA5j=w5(d7!I3YnO1-+$Hl&XFCKd z^%)uSX^0AIz>c!!{a@k|>7CA*bL}wxl{trRJ}a#=w0_ac`G*aO$a=n6kE8v9SjSBR zxSRB=(?*9@bu^yGCl^j&jSq2a=~Z5s2n~Me)!z1+nllgJi}9$exiqbD0#R=M`S3hM zO7)k{=F_mryuH1h*MepXkeg+o7cjD%`Lq#Sa=<>>bmB7X8YdeA9B4r%a?^ZM{^e)J z@i6SPni0}AZS4QYn_4UeyzF9HCq49X{fRRCEd~oiO^e!ITPv}ZXGx_Ob`E!p5gZ(x z?}b5Hn9F~}w6{9KTKdLWxK&%GX1^5>PEBs5nGjwNkdkc2b6=71uR3t`5Tfr@FQF!r z30Yb8(SQ^&zCM3;jAc$i6Yo5Y*J?GAW7%dt$-4)GHw)9OHVyZ97UjTwf8-?r?7P-8 z;9rm7%*S~z;4dd(L(W>=yj9FtOgCPc>E0wR^l>1nd<_ORR+)nKcLVX7w6)ol( zkqAp&FIY>cQ&D`p3EA56&7HN>Yu#;aZTmm_rz0lZIPwbPx-w}$Gx%VaFL>t44wMl{ z0pti5q408XRh{^Pq6cOD`t|WajG$6{A;)%n{QzzaBSUd13!`_Enn}iBPgCA`#i&CL ze&65w;$H;cDtU6iTy8TPR%R@|md^Ku$Ou}AiF5u6hVk_9{{Y2z&HXeC)dY1}2 zEhjrWIGO~o1Va(%csJg;412l~LxbiuIoBEkRF}MvqWDW<;B5;uXy9ssXK>PBn2Gd^ z0ftCln*O_;Hzvj)a_>xCCH)VyFJxACHiqt|_=A6^rCl56B|^B!2c%Cik^=*VjI!_N z(@zw~2$1q+CgSVjR1$_C2s4DeFv$7smX&~fMD%TZs%{yco#yZ7*Av}}zeMkbS!?9$ zx+U_LT?(Q%GTj(*zTAyLSuC224X?EqXw>=VFuvj3CQl&8xt$+w%5qfI>W?Z4zD*mK zNu*Xa(*RqDRJ}pKAPZpFS*U&`z_c9rO4Cp6P@)p$5TW2_lb?N1<(fvaiEt%s80Zaq3=8El<5_@+jOhLX3?W}&Jx_}Cvl&HVF_&3@)28^Sw`m!ZL`l1fbuO5q18P zN5x9)&6_u-1=yEHsCBsk5@BZWwSvub^Buz`g0aBm7zF!nizTpa%&!y3`EsTBHwLN> zf1`Z+yJKA=b{$578Qq|%>8QXyXEIEy5?p#Ih|yh@OhRYK*F-OW@iU_wQj7rze~h!{ z=H{jqE3ouHc)O;(Hz3~YL#b>8R+UwY85PUiU$sPMY$0)kstDX|$Ud1E@Wxq5jPY&x z%ia!lvo3)GL5Ma6Xc#!Iv#)RCc-;8@B_i#AQgY!gkAr6B9PuXOYQK>V6>hr10X+L$ z`73bKA&D5gTP6&q$I8HnGU0cEufxG(tAKE=*^*~#l2BA6n?eDwqA0lhZ68!BEL!sq zxEFn|?TMaD!YI*|9gt`4N})u72QkASd}L1GWS$Yi?Xe_3~^t8zjMJ2M=$oS`#~pDE1CJG7hPlm4J#w=kA5 zMkp$B2Nzz*z7^cf<+1k0Mx6n`rowYjO$ml-4Zn#`l{R5I>0 zT$3T9z}ky9YH~X9H=H2cAR3=hJYL%NaaG);BDOC z37cNV0EyIH27i_RbkZBk`xy}X5db#ikQ_R3H$ymyF5FBjpG5lvDWG^V8X$k@{?Okn z+58TdL?TN-v~P|9Iw>QAI=M!ib0OOF5Va*v*#Y?I0XQ};Q3~~ z5hBb9EHp*-&};$Kr>lCP`Ey|mAhFGvao(dCQ1A0UfMw`p;h&v;e_{*=HP)I5g3OqI zdG{~_O^Hw6#L5bGx$XOB%if~6?Is2@*zJ+G=Pq8n*x4PpglvtjUfyQbr9kwpWoL}L zn41rZYGP1rau;i&tzWw~Hi&^#O*U2~e?)i86h_NqU(BpeLLt17MAB@^w>KAp^p3kX zg^ZiO3B`oonh^q+I)}d?RR6jm>%xQj$uX>_rw1ltprBcbmt1=Ol@`{W=8hJ^2ba4QqB zA%`V7tqyuj*r@JP_18IzO8x!uo{#XYf*rqNR39`SYBpwMNabZ+vV$hZivlBZaZnT> zLspd>eh2V#PF7Zs!$^ycnU$5AFarv|H9LcCH_}2*#b%zxy@M$q$?;rhsJRyc=CsQL z&t?~aM5>5Y!i-KT)Z8W^28|m&JAH32ZY|>?XX!$Ql9hEJV#z}gJzVu`VJp6Vh5+3H zHlMQ0X*~OswkR5$Wo5#A?Km5?|Jg^0L_#$h4P#lLFekB_26?v-RBBCz>42i3Ui&>c)<2 zeXAcnt;sM0eV!_?jhmQL3p8m|mwbcoB{*R!6oVA{Aoi$fO@jqvFl8nC70kwO@qLlV@@hQJmInr+d`zk4uM zZV=KqhC$CFlZ1ghVxXs|XJ#w>n13*)gKY509ZaVH6PW_UA0E17!!RSm8~#gd{Tvh= ztzdkW6;0gE?ypq5gj)<~{Q<6yoF8XG0_d}hwo$qv%$UDkD7Zses|nX8Y&F>q6>%*9 z>b&54e~Y}asL%vW0dS;}7)({ECBR()T{cxgW|6BXWbjIF0Bp15T=q4c%*l3s3KXt* z1D6;tJ0cQ8Cg>Jm!1%Y@apW?%b_cKyg)w~p|K_2B;7su~5FyMtp@dBgFaQie`po(a zWSpUJ3~3-U?V?Uw4?%+RzZ(1Yc&PIK|3j2ZTanu;#z#pvWGj`_sHKu5l}m0@+1=D6 z2}$9ot=%G~o7G*hr4o}Zg2dauzNe?hWr18tLaZSOzU@>lked%G9SAiI zxUvWscv9{mq64UDY`p3<4*tz;R4RFEZ%@0Z`u0Zy8N|{mQ}_mK-#GYv#&xH$|2*XL z&*Wj?-U?UTGA$dhAN5#>0~mfV$_csxO0JR*fH|ynd@>FY1dPo)f1EXodjbWEX2=b|k zV=2I&8yI{fDCtEYlk~2Cg^VClKYj!szdv2O4Bnw}xJ|i7{5fh1F#M7!e>3YKY=k_p z{PW4_2i}~3v)HmQP3>Hy%ITeSU$_JG%1@q3PyP!`g^20Pm;3Yr;#&P*cx>{eeb1H9 zt!PIUQimfrj6iwcWT|z@Tfz1({OBW>aTz)lHr`ZY{DbKW`KYm$}*gyCGPB$70ua?13$&sERvSr9k4W+;$wY+iAitlJp zO-)Uf$_M{MLNhX&>zO~!&kyl?+tO0%2Z7fPsr=CjOLO-|OF_>_OB8~>)<`OI(Jp+? z9syiRMlZf|ry1kpATT13#x8m)HN(db5bP@tA(hP&dv9+E-tlDdpN3zU+$g(XXf$KS zj1?ob-`lKI9x&t+;K5@Q@Xm)Xh>}Jn<@(q~0O0cp9H4n3s50-B0_663`GA~o7n&Tt zK^bq9=i8_4D@r5R`sMGh1}*VOV0JH3%GR8tkV<_ef-xAsdoqAq!7*B6K$esUa-o#_ zfKmo!bQoUP5cp(wMM8=zweXLh|7pJe#T@>}cOC_+!deKv@_iwJ(am8Tk{MkKL^0zn z9q7%EAev;q!JkO*-usysG?Y?5uvXZuZ{w9U#u_zODnE-aU-cs`T`v40v4tAVBGJ?R z-}f-#zjRMkiUj=mQz6BGYrd5z)Au0#CV&`#&)b2H@j)N7zIOF0B++0A#3Lmq{&Bk; z6g{0mcTZ&^#bJiuUOJ05Ugb9xHUWKaFwifo_#c=5?}Y79hdLmvQn_;5s)EgeS0hY< zSgJv0J{VK#RB%jW(3QM#8jWVN?t{|86Mh7te|R4P>Yac9jr{&sdD#dG9iS7u<#|2y zIbX^L$IT7Qr+)cn#+<;!M{$+U@%@XkWNCYaO1sGta(}Z`Ft+Nhoew062#wRx*!V&r z>qjpUpU2~glrL!+VdfNeDaXE|kn_s#V{E_ViaU;{A%%c-9r^>7!7@+9OClmFYU&UA z=%S~5{T;ld1p@9w{ z8uVVhRf$Z-;XNuVKX-%Vwy&;6kMSR8rvK$3xo&QSwcoPJYp|81X|sc<9I+inAU5s= z>5!^xp_}eGj;oO=khYj&*lO$vySFVb-jjtwdsd3Ke9}4wgYT4wVCm+z-hqLnblH*) z5Y{>E&LHQ1dhV|jNx;hS&X+0G+y4q$;~Hp|vaH;Er zKob|OOQmfYtQB3CQGQ3w>@Sp(R8+OCA4QA1@Mkc^IOtn=sFUZF_b5Kcb}wy=0fRTgMzYky%2|Qmz}mlAXr?p3)%P#0*@_`O~Kmyph+D#xMo=PH zRmPr49hNbRi;C>qrQRy>nFx=0I-1p%|tWOMe8%_9-PG2D~@HEV?&ao&l&(|%%-w59FuWPa-UMqc#u!6 zq|^EKt5$s?N=lk%us8&WsUaWUY!#^;eb#bz zhQfA13!l$@n8J?5@uAN8rw{3QdtW~ZSQ~9T6g1$vgZEQWpZNZY{5So3!ha{2ZdN(r z8%o^e+pfjgdlge4`jYwCat8d z{oD+yubGN|jnZ)H%AhASri@27vZqrfV*1P}0(%m=`m(6d3nK-rik=9e(PxI(H4N#J zCgXT)?5D?lNV)W}7gV#C4m>d2u1 z<}n<%u_&Ey^t8CxRlaRaz>lVTSLL~Dt3QY}DUO}r(MP-tzwnf%J#pc~tdOF#n^h5; zbVu7S3!P)P7}ai{b`Gy@IxQQ&${N4D)B3+G??0X8KMwnmzv`N^?0~2Dm*3jlYNIK) zuik#z{X{9}soN+1@7&PM;zwzXF%JiNAN>AtcC+8_tP`=i?U`+{jcGR*s9naUTLS@`|bBEh+QwOQp z!u18XgY_9?Ah#%Wt%}l#FITj_$IYb2{PkpFRTY!?^~ zf7r|4L)o+=zN#T7(bWY;BO2@E?}Xkx=+ZmukqV z!r_@v7BY!EwnDn+I{sVIm}h9EtxCiEZr!gjBP$E1_yI=(KGhpL`qzaYY&;u@+KdAtKY`> z4Dwoej6PQNnPEAyo`=K&?vRyieJYa4j&4*vS4%sE>Bq}A+9J36ST5!` z7?(&TNK;$cOHCv;xpYHhAqx!&f!9wktWjO?lIbEFJ7tX7jONO+21vPNdLm_?-%gK` z3r-NU*Fe}3)o1YCq}%{hG))shKtZsfp)YA&hA$7_)FJtxQ`d zqcy4Ma(r-%_ms9)$~$$*A01L;adYq$ROEgRQI70+sH6&sj+D$7;MF91JEl!F&L8Ht z;nm}zBKm=LCblsdIjiA`3fo@B!kCAo5y@_!CQqvzcfDjdsodsqh0M8vdg|qX%;`q2 zkAKB;FQpy#Kg;OYWH2T+Ke4rhc%LdA6iXEzAG{fXYSUO&V}b*C(q2DontGh}crJEp z_6TRU${9*mq5p~YiL|c?pbjevlB7JcX~3qA!6_6%C}|X5M$H3kTi0-f`yBqwrt0eI zvlkuKky(rvM7~mhS3ohy{-!0>G#V;mgh3SMH#PZ%y~7E9+8G5tK71!o=iEuObp&pk z7KE%inAeJBt~qFVi@M5H!B}^9!D(7W><}lcO}AZP^;V#X*|rgzAux>C%x|^HII{CR z&9LjsFa}e7qqebz?An3O4A>-D>8oB!z;<^K+L*sy>vBAMD8J|)Zm8~5KqO9BD0F>N zP(~}TD8O${JjV?$!K-^131g{3{*Yvdj#DMNv#5-ERG;a-;E>|g0BsiKdH_YI!wX=c zHsT}|yns%Eu->6jQ?=)roEDQsof=NHorr9mELkN0o~Ek@SY5j8!u4AMfDYEBgt6b#u|TYGKPLA2E|K0Olo z^fMG-;(5JL++TTSG+D}vyVwMOfM&lStPd_S+I~mfEeG;VGC8C>&Nhz+g#A*)?@&a& zBg%*8DweHKWIX0;s?Y*C~!gC$KI3Fj~en< zKJ7!~;;M%4{|&pIJQCrLVKaYmaayze9|YjP-M*D$Q9HZ0pl~=Qsc3EdT~+MH0z+x0 z?5;`g#qM4hUQ+28X6+^<8ISmPrkUMU_mM`0#XBLWT5qzmvm4CI4wtR3V|bu;(Gl}p zdJ)Oa$+=L^kWBwXB}m{d{z?&DQ-1xka5GMSWjUR4&A|gsnxk+Y(P*KFN>Zc>8M>(N zI!ZvHx+voaBVmMl3W9mmdB>T8Blssl$vs@M(qgB~Jy3s3c@@rjL;PO2VW=a%NEA}q zarSGRJ8reAM}I->Q1L?j=^gFux!S7f-RwMvyLzMSIL>%#IdwO`$Q!$)HKErDU3jc) zS8H*`LY$Db4uZGb^w>;l|jgUWuqzeirq5mBIR8NIYfUY z6}j*;!!Hq~rl$6MNJd*bKu~vsvSg?;aF?b)g74l0Da~!tSGZYW2cPeS4c%(UoYh*%31B1ecobm+7-CMj`qf<<3XY_t|YwDWX-=l z<9~auLvCh<1Fli^cKT*fBgd??WuoPLUS#=Y9FP#Ck4Nx!A%h;P;VdgH3R0HrlVQ7} z-9M2$G`66HaiUbR^uV{ud;p~&x`>N zg?9&`1+P$GhYTuG%WQZE8g9?F4&6f+;^B9T?Ws)kdX>J)5k6CT6bIYb? z&F;my_YfiB&h(!870_piPVkITc4Sc{$ywo1JJ4Phofe(aiXDf;uPBNPv zL=em?okXB!XKutEiHX^iG7c^?WqDi4j%B^T3{{{|@DQpAcjiwFZkMrw+Dda4&zaD@ zymU$#N4FP5z-?(wPdxo1KBwwpibop{kkM193*kgkJJGW~KJII1C}+-=Lkg~|R&auh z_vyEp)IMG_n?cMmv(~Nk3h52^3($#gT(!(ytx8>q)|}%Csn7!lycUkRb{BLHDRC_c z$G%C&(M7o$?y|qXf3VdZ^-WN7e;auKroOCgY%Iw*x=E@Nno|G(NKU`sA;on+GYo}3 zJYpt-R?-D`s?faOzZDSvXxruRAY?JJ@|#vT!$_b3rzT|SBG1XKPmy#!MocVwp|pmj zaqFsB>7^2}ZBgmgw8k51t+tUNx%{d2HoB+j79kl_lR~j&X6-M!LI1x||GU)-dDDVt zwDY@qyPQeC*An}%{mrZw&p4HKN#t;+>wK-b`YPq6_MaI+?|HnTPocNz>&BZp*>-0X z31c)CXF;QHH!B%0_}CemNer&pjSr4^7ds(`HUP9s&HyDYC-Pq%lrwKYmUqPEZ zv4N>_NPu=O@)?(k+JCOXvmb8~(Mwew=VA6WpHPT4R&*MoGrmx(cGW(sylDD?yde>_ z_MN88K;Sufi)6=7wvN|m>Xl!Und#QY?awp3_n#+n?wJ{q7UlBk0o1<7=h(4{WTevY za*6vIw~|_E0hGmUdj2Y}5#Fdk8MQ!Ix@(Tb&J?l9&j)?-`ksQ}mRP^Q72GT}xq=_| z-T2`_P-EII&!tul@|ivgK;iD;edV|&>IIfNXfku=!?WvVRg773P)kdo{wQSl`sIbl zM2KStA!jV~KH=|@lYBx>ds)W6=18I$O+J&i>|=$rK&trm{TZpa zoQz3QIM4^(r+9x2TXFcs(WQg6JGS(2fQMs?sdH?$Eq5G%R@lQ+aRqM@!Hu4@IQ&Ye z#TJtpPuXN$@bBgEE)kR}9{JaDFJir=9ZwZBv6$okWj9qAt92D3w=x~_{Dy#Ld~jHD-M_G#peM;(@ah8R^e~Xieol7leFZkSHv4NhgwtIP&VrSn_^A$Q>U%`+Tu{V1zxL zT$)+a!!CXG>Xi%R##7iKS=!B;ce9fLMAeT0N!sfNl&YKU9wG0OR-eM@BRS{M)q+j% zwEj3m_4(AH1TwW=d8aEV6Ov7JOIh;XSK62Z_3`l@7a`Xf_0V1R@#30-k$(qEHD=QU z;Z>2Vmu_EEsn*W(X%-0+3~pD>{PLr7_~y8?Gx@e5C0!v7b4w>q#k_enU1g&Sc4sZd zef*fu+|g0cYU52f#f}Q5;CT(2RBvJ(1t6eWWD`ZG;iIY7seQ;a9nDh5>FI+WsNr>W zL7}CkWd%=l@~?kh8FMyAt~Cf6Ra5GM-(3_l1h}ZpSr_Hm)bZPh5ORlO^$LtSl#gB<+V+E^l${|UdTVT+eac_{ctHH&r@T+ll(}dSF>b{&Dk28LC zymsPvb6#5g-u4mROx6NTq$>i%l4a}BEO987=lv$@Q$t`g?`Z1yn=>4XRHD*ngy+Qx zX(L@XBX4$~+b@6aTI(qoWBgIiXLX8R5MI7c3SNoB7{18pVT?kWYdZ!>k-17A+n`W- z*jTs()SlEb=o3#l8E1$E-*EWG%trMzTeViY)i}PXnVqdJ_0gNw^o(Co!C&`|%X`Hg z?^B!%%lvP^`tLd5ADmX2r}55tY?;Sp@0nXRPO37Y0{LyF$r;xeoaCV>h91@M+xqdW zH-m8l0&^lr(XiS$X)8e;dQLA=SPX-oZMVj=k3~kNoE!m|Rt>^z$DhAW-I$g(V<@za z0O8EaSrMgAE@kr;$b@1}!BgQ-irzTi3*g^SCxk$#N_lHi_TJSbo!rwMvVZ^f192Qh zmW>TowGCS`XO0SZ`d#!x!}LeX8S^LBewf>$Br478T3O5#z@RoZQ+`@hrOyHdN3g8mq~i->bwD|KzH>|Vm0~J$*@x^&bI{TxD zrrAP{t;KUbsjJAd$z|Ymo$SMBDBDJUy3*02B4y0YH=6L}GreAv6uyQ4bN7@G?R>K& zwC2zKucvfFt%gU`$c;n{u`KH=9;5VP7-@lJASwKEx$Z7;z6 zEpv2kps*b0*ijh5e>qNf@8{($uVlz`&CFP%-gw0LGG{qc(b@TrP-BY{mxNh&$hEig1}T4Gs4>6OwZ`YmQP8nfFni0P0d8@9(TVsE z)*`yinOoy;pghBN8Uu9sMA@PVqTr@` zzs-{|pAnm+yy|QIzz(1k^mf8;R#e|iNVdJuEIR;;Dz@+a>-vSc+pvh44E1R&@z@9N z`4hL`dL7W?Lv>bbtL?@&sYq{5!4oFM)zCr*wZ0^sw+hT^J6__i1J()R0^SktStsHg z7C$L4a~pp;#QFM zaN_|Oz4c>-*n1t*8w}T>8>p9$j~j;G6y~HKm#+C8f6X-r|FS-ZspwQCYU&XT$^8Wm zrz^0!=R=X;lst zbmaP&D;StE8!^scr3C=%^X_Xn`VmtcNOqJn$#c~_1xk;@3R^1Xg=|wCNUr_b6 zDl_{UBTJpNMbOM$v!RN&C$Q16)JspYwBQ~4@Jdo_Yg@^Pg{+wOfgM{Y&-1*FJLH5* znVSqDB@sAUoo$jqMk-*0a^p!{GWh8w(D<`+8J1G!plUkqA(Vvg1I7H?ZO#<)vl97U zcfNsq!~dS6Y_nIoE7DfKYk`)jm%&Rmwp&rqsRvss#@;?-;xhM3`PSI!kZ6^7*;N2* zcQ0v8Ba8|+q*-dbmM>k?iaO(%g}c6da}Fzx+o`M6-KN<*T2j!_CAq$QwV{0J97BcG zSQdM;5RLR~(;tH3lzl4kes97U?=r|gXDDOfoV?4Nb&6}dW_S>9F88jmTY-ub(6*`` z<8&#z-3L1u8QHL)0x_H6dVf}nk9NjZu3R})Yr>OcCncedU|VBV5=Lpviuz3If)6cm zLR;!ik6nQ9+$rtnnqOgW$7#Pf&Nxm)GwW1DD?I-?PKTL)ltR;lk13|TSGUo4l9n`_EN{(UJwS%SroGwbkE4m9I8ME zE%iQyH3l*mnIW4XUR~3R_rY$h5Jh5<&X}=bqH2Ucxc#vixvP`IzAKYZ`oZA=e7Ug? z?Mv4FV|mbh=_xi5u*WX6D(f|Xq(@;OYu|S>r(_*FQ%CKK zIa|*VLjkyc^l*fn8!40T_DdZ9?2>mi2om1r&71cLo`fMAU#wM%dcJ4p+@7cI*zU?! zUG%fh$`muA_T@LG{ym6RSX0-s5iR}F` z5&1r4s>FAdFTqTf>8nU5f+RMWXdZWe(XA2ZwbD>o^zE;C_T3wAWeY=hG5WaIE+$G# zZ|3hNDs9ZA%s`$G{$_{M2(QErieHjhnkdeI{|s{k**rfOKDN>uD_6rtdp;~{H^<2Nxhm#T)}QS07j%pa+^=IvO!B=u|b8B8@qgSbt^XVZPL1_gDY*+YL{z|MBB!%u%6#qFwdaX@{7q_iT}%jG?tu^MluEF zrJU+FZ1?-b=5C&JDsA=l%%cxm`5nW&Grw8Mm~UtSQg_~m&a^urDvmA+UbG$wRfK&I zAdemVrjBIH$7)M*=3!T-GbY_?kFLOaZS>VTN_ML_2Ap>^tV}e}i&!x!=xbbHbj?ptms1Lg1&dqn^csPpwi$FMg)>a=9p*r{gfYO~8z zc68N^_rVLOK7ZviM9&hiCx${UEufr&w--f zv@r-0#_U}m)v=;I7AoBrA*F{z8^?c}lA!V~b8Jp>B(xnk7-~O#HnQ*d-SDsAm!KZ( zyrQc#f3wyVWjX9DaJ~}&uXsI~lhOBX{ z53kamc(tsEF=-|0b8+0fFBYYExxvA%6QhOT1i>L^K8_H1d^v#f6R3UNBw zfV}O=H%~75@JAM9k~iM>Z^dqn_5oJSTA;c`In5<18eSXU^RsKg){PSu{alnDUp3bN zd#A7Y&URp{L+xGbFx4XVbJ4u^P#xtl^%*rN_>izvM*t`4nNa% zOpBg}{ZxO)=vI07%_f3*$nlk+V$oN8CZ?)?d~U12$JB30?d;*c7;!V%%bqT8Nac)Y zWhcF2wBIc2Vb?G89O0d~p)Q+(`n0n@&n1$iLUS{)23MWZ`kUw%Ga=SIc9$X_mvxO)lNu^6x6*vL#i)IKSqtGj3IKZaIG> zUW>fJeY`;>zNTrr+AUkRQqh;Y3NC^UIUfZ*QjGxp9bs1cjdcawaYWHU>KshC#sHcQM>2&&~>Y0ySnZFs+#<3 zCqNlv!>z<{V(>e`3fU@7O`OTl&`^PHL-BG&-k`2O<$Jdw&#L{r)$DS5TeXbf9j55NdN!< 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..5d5dd2689 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { "name": "zenstack-monorepo", - "version": "0.5.0", + "version": "1.0.0-alpha.26", "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..16daeba1e --- /dev/null +++ b/packages/language/package.json @@ -0,0 +1,32 @@ +{ + "name": "@zenstackhq/language", + "version": "1.0.0-alpha.26", + "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..e01e0c7a1 --- /dev/null +++ b/packages/next/package.json @@ -0,0 +1,37 @@ +{ + "name": "@zenstackhq/next", + "version": "1.0.0-alpha.26", + "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..a3f3eb15e --- /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.26", + "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..b8250da86 --- /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.26", + "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..623ca2180 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.26", "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..fc8e8d017 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.26", "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..b3ec1e91a --- /dev/null +++ b/packages/sdk/package.json @@ -0,0 +1,30 @@ +{ + "name": "@zenstackhq/sdk", + "version": "1.0.0-alpha.26", + "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 cf14a748649a32a58a4f04d6103e0b193b3fb6be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687040 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 {X-VDAt9Y;7N;&l)E-m4@Z;`&g5 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�LvOWeU^)KgJz- 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

Ybz0OL0A{;2x>mk_qA1^p%5;LzvK_eMs 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

    DCvj+wC-jYVk=2l5yh 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

    MJG} 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

    q?dn*!H`4ZVZ;BpHh`lJKeSWqofxyUrai% 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 M*nAT#{KMq`2e42*a{vGU diff --git a/samples/todo/public/avatar.jpg b/samples/todo/public/avatar.jpg deleted file mode 100644 index 34e82838c53e49f6edec915a2e3d25874c3db92d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29019 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 F|Ji%HlGy+N diff --git a/samples/todo/public/favicon.ico b/samples/todo/public/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/samples/todo/public/logo.png b/samples/todo/public/logo.png deleted file mode 100644 index 48bbe06d95a31bcd0e3db4c18cd60de37d1579c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11466 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 - - \ 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 ( +

    + ); +} 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), + }; +} From d2767f2ffb6713e620ce99b6cf5408d104b91074 Mon Sep 17 00:00:00 2001 From: Yiming <104139426+ymc9@users.noreply.github.com> Date: Fri, 27 Jan 2023 23:00:29 +0800 Subject: [PATCH 2/2] chore: bump version (#187) --- package.json | 2 +- packages/language/package.json | 2 +- packages/next/package.json | 2 +- packages/plugins/react/package.json | 2 +- packages/plugins/trpc/package.json | 2 +- packages/runtime/package.json | 2 +- packages/schema/package.json | 2 +- packages/sdk/package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 5d5dd2689..3ab50a562 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-monorepo", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "description": "", "scripts": { "build": "pnpm -r build", diff --git a/packages/language/package.json b/packages/language/package.json index 16daeba1e..a7da6be48 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/language", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "displayName": "ZenStack modeling language compiler", "description": "ZenStack modeling language compiler", "homepage": "https://zenstack.dev", diff --git a/packages/next/package.json b/packages/next/package.json index e01e0c7a1..887edb7c2 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/next", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "displayName": "ZenStack Next.js integration", "description": "ZenStack Next.js integration", "homepage": "https://zenstack.dev", diff --git a/packages/plugins/react/package.json b/packages/plugins/react/package.json index a3f3eb15e..cdef9b1ef 100644 --- a/packages/plugins/react/package.json +++ b/packages/plugins/react/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/react", "displayName": "ZenStack plugin and runtime for ReactJS", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "description": "ZenStack plugin and runtime for ReactJS", "main": "index.js", "repository": { diff --git a/packages/plugins/trpc/package.json b/packages/plugins/trpc/package.json index b8250da86..bbdd85552 100644 --- a/packages/plugins/trpc/package.json +++ b/packages/plugins/trpc/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/trpc", "displayName": "ZenStack plugin for tRPC", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "description": "ZenStack plugin for tRPC", "main": "index.js", "repository": { diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 623ca2180..82fe4c3ba 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/runtime", "displayName": "ZenStack Runtime Library", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "description": "Runtime of ZenStack for both client-side and server-side environments.", "repository": { "type": "git", diff --git a/packages/schema/package.json b/packages/schema/package.json index fc8e8d017..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": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "author": { "name": "ZenStack Team" }, diff --git a/packages/sdk/package.json b/packages/sdk/package.json index b3ec1e91a..95cfdc3ae 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "1.0.0-alpha.26", + "version": "1.0.0-alpha.27", "description": "ZenStack plugin development SDK", "main": "index.js", "scripts": {

    9O9s!$B{OL9=j)K= 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