From fa807e4919318cb415337ca85f0a270484f52780 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:51:52 +0800 Subject: [PATCH 1/8] add zenstack --- README.md | 149 ++- .../20230620053259_add_policies/migration.sql | 49 + api/db/schema.prisma | 91 +- api/package.json | 6 +- api/schema.zmodel | 65 ++ api/src/graphql/comments.sdl.js | 2 +- api/src/lib/db.js | 9 + api/src/services/comments/comments.js | 21 +- web/src/components/Comment/Comment.js | 18 +- yarn.lock | 986 +++++++++++++++++- 10 files changed, 1315 insertions(+), 81 deletions(-) create mode 100644 api/db/migrations/20230620053259_add_policies/migration.sql create mode 100644 api/schema.zmodel diff --git a/README.md b/README.md index 1e8bddc51..3c661055f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,143 @@ # Redwood Tutorial App -This repo represents the final state of the app created during the [Redwood Tutorial](https://redwoodjs.com/tutorial). -It is meant to be a starting point for those working on the second half of the Tutorial, starting at the [Intermission](https://redwoodjs.com/docs/tutorial/intermission). +Follow these steps to get started with ZenStack: -This repo contains much more styling than the one we built together in the tutorial, but is functionally identical. +1. Prepare project -## Setup + Install CLI -The [tutorial itself](https://redwoodjs.com/docs/tutorial/chapter1/prerequisites) contains instructions for getting this repo up and running, but here is a summary of the commands: + ```bash + cd api + yarn add -D zenstack + yarn add @zenstackhq/runtime + ``` -```bash -git clone https://github.com/redwoodjs/redwood-tutorial -cd redwood-tutorial -yarn install -yarn rw prisma migrate dev -yarn rw dev -``` + Bootstrap ZModel from `schema.prisma` + + ```bash + cp db/schema.prisma ./schema.zmodel + ``` + +2. Prepare model + + Add the following section to `schema.zmodel` to output the generated Prisma schema to the default location of Redwood: + + ``` + plugin prisma { + provider = '@core/prisma' + output = './db/schema.prisma' + } + ``` + + Run `zenstack generate` and verify that `db/schema.prisma` is updated. + + ```bash + yarn zenstack generate + ``` + +3. Add access policies + + Note the added `@@allow` rules (all operations are denied by default). + + ```prisma + model Post { + id Int @id @default(autoincrement()) + title String + body String + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + + @@allow('read', true) + @@allow('all', auth() == user) + @@allow('all', auth() != null && auth().roles == 'admin') + } + + model Contact { + id Int @id @default(autoincrement()) + name String + email String + message String + createdAt DateTime @default(now()) + + @@allow('create', true) + } + + model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + hashedPassword String + salt String + resetToken String? + resetTokenExpiresAt DateTime? + roles String @default("moderator") + posts Post[] + + @@allow('create,read', true) + @@allow('update,delete', auth() == this) + } + + model Comment { + id Int @id @default(autoincrement()) + name String + body String + post Post @relation(fields: [postId], references: [id]) + postId Int + createdAt DateTime @default(now()) + + @@allow('read,create', true) + @@allow('delete', auth() != null && auth().roles == 'moderator') + } + + ``` + + Rerun generation and migrate the database. + + ```bash + yarn zenstack generate + yarn rw prisma migrate dev + ``` + +4. Switch to relying on access policies for authorization + + Remove authorization from `api/src/services/comments.js` + + ```diff + export const deleteComment = ({ id }) => { + - requireAuth({ roles: 'moderator' }) + return authDb().comment.delete({ where: { id } }) + } + ``` + + and `api/src/graphql/comments.sdl.js` + + ```diff + type Mutation { + - createComment(input: CreateCommentInput!): Comment! @skipAuth + - deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator") + + createComment(input: CreateCommentInput!): Comment! @skipAuth + + deleteComment(id: Int!): Comment! @skipAuth + } + ``` + + Change UI to allow everyone to delete comments: `web/src/components/Comment/Comment.js`. + + ```diff + - {hasRole('moderator') && ( + + -)} + ``` + +5. Test it + + Now if you delete a comment with a moderator role, it should succeed. A failure will be generated for other roles. + + The error thrown is of type `PrismaClientKnownRequestError`. It seems by default Redwood's GraphQL server captures it and returns a generic error message. Need to figure out how to set up a global error handler to turn it into a proper "Forbidden" error. diff --git a/api/db/migrations/20230620053259_add_policies/migration.sql b/api/db/migrations/20230620053259_add_policies/migration.sql new file mode 100644 index 000000000..f0da32de4 --- /dev/null +++ b/api/db/migrations/20230620053259_add_policies/migration.sql @@ -0,0 +1,49 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT, + "email" TEXT NOT NULL, + "hashedPassword" TEXT NOT NULL, + "salt" TEXT NOT NULL, + "resetToken" TEXT, + "resetTokenExpiresAt" DATETIME, + "roles" TEXT NOT NULL DEFAULT 'moderator', + "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, + "zenstack_transaction" TEXT +); +INSERT INTO "new_User" ("email", "hashedPassword", "id", "name", "resetToken", "resetTokenExpiresAt", "roles", "salt") SELECT "email", "hashedPassword", "id", "name", "resetToken", "resetTokenExpiresAt", "roles", "salt" FROM "User"; +DROP TABLE "User"; +ALTER TABLE "new_User" RENAME TO "User"; +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); +CREATE INDEX "User_zenstack_transaction_idx" ON "User"("zenstack_transaction"); +CREATE TABLE "new_Comment" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "body" TEXT NOT NULL, + "postId" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, + "zenstack_transaction" TEXT, + CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Comment" ("body", "createdAt", "id", "name", "postId") SELECT "body", "createdAt", "id", "name", "postId" FROM "Comment"; +DROP TABLE "Comment"; +ALTER TABLE "new_Comment" RENAME TO "Comment"; +CREATE INDEX "Comment_zenstack_transaction_idx" ON "Comment"("zenstack_transaction"); +CREATE TABLE "new_Post" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, + "zenstack_transaction" TEXT, + CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Post" ("body", "createdAt", "id", "title", "userId") SELECT "body", "createdAt", "id", "title", "userId" FROM "Post"; +DROP TABLE "Post"; +ALTER TABLE "new_Post" RENAME TO "Post"; +CREATE INDEX "Post_zenstack_transaction_idx" ON "Post"("zenstack_transaction"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/api/db/schema.prisma b/api/db/schema.prisma index 154619e09..a7d3000ff 100644 --- a/api/db/schema.prisma +++ b/api/db/schema.prisma @@ -1,48 +1,75 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + datasource db { - provider = "sqlite" - url = env("DATABASE_URL") + provider="sqlite" + url=env("DATABASE_URL") } generator client { - provider = "prisma-client-js" - binaryTargets = "native" + provider = "prisma-client-js" + binaryTargets = "native" } +/// @@allow('read', true) +/// @@allow('all', auth() == user) +/// @@allow('all', auth() != null && auth().roles == 'admin') model Post { - id Int @id @default(autoincrement()) - title String - body String - comments Comment[] - user User @relation(fields: [userId], references: [id]) - userId Int - createdAt DateTime @default(now()) + id Int @id() @default(autoincrement()) + title String + body String + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + + zenstack_guard Boolean @default(true) + zenstack_transaction String? + + @@index([zenstack_transaction]) } model Contact { - id Int @id @default(autoincrement()) - name String - email String - message String - createdAt DateTime @default(now()) + id Int @id() @default(autoincrement()) + name String + email String + message String + createdAt DateTime @default(now()) } +/// @@allow('create,read', true) +/// @@allow('update,delete', auth() == this) model User { - id Int @id @default(autoincrement()) - name String? - email String @unique - hashedPassword String - salt String - resetToken String? - resetTokenExpiresAt DateTime? - roles String @default("moderator") - posts Post[] + id Int @id() @default(autoincrement()) + name String? + email String @unique() + hashedPassword String + salt String + resetToken String? + resetTokenExpiresAt DateTime? + roles String @default("moderator") + posts Post[] + + zenstack_guard Boolean @default(true) + zenstack_transaction String? + + @@index([zenstack_transaction]) } +/// @@allow('read,create', true) +/// @@allow('delete', auth() != null && auth().roles == 'moderator') model Comment { - id Int @id @default(autoincrement()) - name String - body String - post Post @relation(fields: [postId], references: [id]) - postId Int - createdAt DateTime @default(now()) -} + id Int @id() @default(autoincrement()) + name String + body String + post Post @relation(fields: [postId], references: [id]) + postId Int + createdAt DateTime @default(now()) + + zenstack_guard Boolean @default(true) + zenstack_transaction String? + + @@index([zenstack_transaction]) +} \ No newline at end of file diff --git a/api/package.json b/api/package.json index 2edfead03..76b0f4613 100644 --- a/api/package.json +++ b/api/package.json @@ -4,6 +4,10 @@ "private": true, "dependencies": { "@redwoodjs/api": "3.2.0", - "@redwoodjs/graphql-server": "3.2.0" + "@redwoodjs/graphql-server": "3.2.0", + "@zenstackhq/runtime": "^1.0.0-beta.2" + }, + "devDependencies": { + "zenstack": "^1.0.0-beta.2" } } diff --git a/api/schema.zmodel b/api/schema.zmodel new file mode 100644 index 000000000..6106b17a4 --- /dev/null +++ b/api/schema.zmodel @@ -0,0 +1,65 @@ +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +plugin prisma { + provider = '@core/prisma' + output = './db/schema.prisma' +} + +generator client { + provider = "prisma-client-js" + binaryTargets = "native" +} + +model Post { + id Int @id @default(autoincrement()) + title String + body String + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + + @@allow('read', true) + @@allow('all', auth() == user) + @@allow('all', auth() != null && auth().roles == 'admin') +} + +model Contact { + id Int @id @default(autoincrement()) + name String + email String + message String + createdAt DateTime @default(now()) + + @@allow('create', true) +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + hashedPassword String + salt String + resetToken String? + resetTokenExpiresAt DateTime? + roles String @default("moderator") + posts Post[] + + @@allow('create,read', true) + @@allow('update,delete', auth() == this) +} + +model Comment { + id Int @id @default(autoincrement()) + name String + body String + post Post @relation(fields: [postId], references: [id]) + postId Int + createdAt DateTime @default(now()) + + @@allow('read,create', true) + @@allow('delete', auth() != null && auth().roles == 'moderator') +} diff --git a/api/src/graphql/comments.sdl.js b/api/src/graphql/comments.sdl.js index 2dd153d03..156c64cd5 100644 --- a/api/src/graphql/comments.sdl.js +++ b/api/src/graphql/comments.sdl.js @@ -26,6 +26,6 @@ export const schema = gql` type Mutation { createComment(input: CreateCommentInput!): Comment! @skipAuth - deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator") + deleteComment(id: Int!): Comment! @skipAuth } ` diff --git a/api/src/lib/db.js b/api/src/lib/db.js index 5006d00aa..993a14913 100644 --- a/api/src/lib/db.js +++ b/api/src/lib/db.js @@ -2,6 +2,7 @@ // for options. import { PrismaClient } from '@prisma/client' +import { withPolicy } from '@zenstackhq/runtime' import { emitLogLevels, handlePrismaLogging } from '@redwoodjs/api/logger' @@ -14,6 +15,14 @@ export const db = new PrismaClient({ log: emitLogLevels(['info', 'warn', 'error']), }) +/* + * Returns ZenStack wrapped Prisma Client with access policies enabled. + */ +export function authDb() { + console.log('Context User:', context.currentUser) + return withPolicy(db, { user: context.currentUser }) +} + handlePrismaLogging({ db, logger, diff --git a/api/src/services/comments/comments.js b/api/src/services/comments/comments.js index 7eface794..7a9ba5f8d 100644 --- a/api/src/services/comments/comments.js +++ b/api/src/services/comments/comments.js @@ -1,31 +1,36 @@ -import { requireAuth } from 'src/lib/auth' -import { db } from 'src/lib/db' +import { authDb } from 'src/lib/db' export const comments = ({ postId }) => { - return db.comment.findMany({ where: { postId } }) + return authDb().comment.findMany({ where: { postId } }) } export const comment = ({ id }) => { - return db.comment.findUnique({ + return authDb().comment.findUnique({ where: { id }, }) } export const createComment = ({ input }) => { - return db.comment.create({ + return authDb().comment.create({ data: input, }) } export const deleteComment = ({ id }) => { - requireAuth({ roles: 'moderator' }) - return db.comment.delete({ + // instead of checking roles explicitly, we now rely on the + // access policies to authorize the operation + // + // requireAuth({ roles: 'moderator' }) + // + return authDb().comment.delete({ where: { id }, }) } export const Comment = { post: (_obj, { root }) => { - return db.comment.findUnique({ where: { id: root?.id } }).post() + return authDb() + .comment.findUnique({ where: { id: root?.id } }) + .post() }, } diff --git a/web/src/components/Comment/Comment.js b/web/src/components/Comment/Comment.js index d6ae550e8..d737b1d13 100644 --- a/web/src/components/Comment/Comment.js +++ b/web/src/components/Comment/Comment.js @@ -43,15 +43,15 @@ const Comment = ({ comment }) => {

{comment.body}

- {hasRole('moderator') && ( - - )} + {/* {hasRole('moderator') && ( */} + + {/* )} */} ) } diff --git a/yarn.lock b/yarn.lock index 62c174f70..024a36428 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1777,6 +1777,41 @@ __metadata: languageName: node linkType: hard +"@chevrotain/cst-dts-gen@npm:10.4.2": + version: 10.4.2 + resolution: "@chevrotain/cst-dts-gen@npm:10.4.2" + dependencies: + "@chevrotain/gast": 10.4.2 + "@chevrotain/types": 10.4.2 + lodash: 4.17.21 + checksum: 2ced8f490c6df32833d7d8f3ac4cf5f17d1419f023070cb8be0ad5324db80f09a8c21165724805e5182d0a94fd9b0a05f32be7623036c19ae24a97064b033d8c + languageName: node + linkType: hard + +"@chevrotain/gast@npm:10.4.2": + version: 10.4.2 + resolution: "@chevrotain/gast@npm:10.4.2" + dependencies: + "@chevrotain/types": 10.4.2 + lodash: 4.17.21 + checksum: fec0173ba67fccec5920b15014a81a9f75e29a4f13cd2129b10887130f8bf9c1c34b0b97c32b9d8528ab98c79d56bfca18a2b4514c0b2e361f84eb7d9fb1627c + languageName: node + linkType: hard + +"@chevrotain/types@npm:10.4.2": + version: 10.4.2 + resolution: "@chevrotain/types@npm:10.4.2" + checksum: 4ecbcb2e92bd9cd78302efb94f65d5a55de7226f9bbea7341def31b11b82e0ef694c062b1d0bbde098187071b24db1815b97006dca14cb86cf232eae03bba67b + languageName: node + linkType: hard + +"@chevrotain/utils@npm:10.4.2": + version: 10.4.2 + resolution: "@chevrotain/utils@npm:10.4.2" + checksum: 085f62a6c9144c859cb59a9d7585b64965fb1958e60a5cd59aa78f88dc70ac993f156a80159ef2c4fc78b07443a804ea8620f8abb7ade10ac0e211d5dc137ed3 + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -3118,6 +3153,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:^1.1.5": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 86512713aaf338bced594bc2046ab249fea4e1ba1e7f2ecd02151ef1b8536315e788c11608fafe1b56f04fad1aa3c602da7e5f8e5fcd5f8b0aa94435fe65278e + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -3199,6 +3241,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/api@npm:1.4.1": + version: 1.4.1 + resolution: "@opentelemetry/api@npm:1.4.1" + checksum: 5ee641d3d64c91e87ee328fc22251fc70c809a3c744e51e595ca77c0bd3cad933b77a79beb4dac66b811e5068941cef9da58c1ec217c0748a01f598e08a7ae66 + languageName: node + linkType: hard + "@opentelemetry/api@npm:^1.1.0": version: 1.2.0 resolution: "@opentelemetry/api@npm:1.2.0" @@ -3256,6 +3305,15 @@ __metadata: languageName: node linkType: hard +"@paralleldrive/cuid2@npm:^2.2.0": + version: 2.2.1 + resolution: "@paralleldrive/cuid2@npm:2.2.1" + dependencies: + "@noble/hashes": ^1.1.5 + checksum: 28ef7ed82d031a660b2ab32009c71c864da44002d06f556037aa55afca573fd44ea25f38f0b3408e447aa2398d3bb91ca638f5bff124f50470a92808fafaa40f + languageName: node + linkType: hard + "@peculiar/asn1-schema@npm:^2.1.6": version: 2.3.0 resolution: "@peculiar/asn1-schema@npm:2.3.0" @@ -3328,6 +3386,33 @@ __metadata: languageName: node linkType: hard +"@pnpm/config.env-replace@npm:^1.1.0": + version: 1.1.0 + resolution: "@pnpm/config.env-replace@npm:1.1.0" + checksum: 4cfc4a5c49ab3d0c6a1f196cfd4146374768b0243d441c7de8fa7bd28eaab6290f514b98490472cc65dbd080d34369447b3e9302585e1d5c099befd7c8b5e55f + languageName: node + linkType: hard + +"@pnpm/network.ca-file@npm:^1.0.1": + version: 1.0.2 + resolution: "@pnpm/network.ca-file@npm:1.0.2" + dependencies: + graceful-fs: 4.2.10 + checksum: 95f6e0e38d047aca3283550719155ce7304ac00d98911e4ab026daedaf640a63bd83e3d13e17c623fa41ac72f3801382ba21260bcce431c14fbbc06430ecb776 + languageName: node + linkType: hard + +"@pnpm/npm-conf@npm:^2.1.0": + version: 2.2.2 + resolution: "@pnpm/npm-conf@npm:2.2.2" + dependencies: + "@pnpm/config.env-replace": ^1.1.0 + "@pnpm/network.ca-file": ^1.0.1 + config-chain: ^1.1.11 + checksum: 71393dcfce85603fddd8484b486767163000afab03918303253ae97992615b91d25942f83751366cb40ad2ee32b0ae0a033561de9d878199a024286ff98b0296 + languageName: node + linkType: hard + "@polka/url@npm:^1.0.0-next.20": version: 1.0.0-next.21 resolution: "@polka/url@npm:1.0.0-next.21" @@ -3349,6 +3434,17 @@ __metadata: languageName: node linkType: hard +"@prisma/debug@npm:4.15.0": + version: 4.15.0 + resolution: "@prisma/debug@npm:4.15.0" + dependencies: + "@types/debug": 4.1.8 + debug: 4.3.4 + strip-ansi: 6.0.1 + checksum: ad511e2e32e3d9cac0186f9f8d7e5d0dca910531b643d5259c5ca5dd4e014b87e161516037a2e1d3741eca05782c1d44bffc3325897f6fe4b188bd2c6dc31d16 + languageName: node + linkType: hard + "@prisma/debug@npm:4.3.1": version: 4.3.1 resolution: "@prisma/debug@npm:4.3.1" @@ -3389,6 +3485,13 @@ __metadata: languageName: node linkType: hard +"@prisma/engines@npm:4.15.0": + version: 4.15.0 + resolution: "@prisma/engines@npm:4.15.0" + checksum: c57c37f13c77b68e2dc8015c8fbf797b628516ee4db18d9eeffa8a5da3e40403d4d31956569680cb548e7ececcc0f039a31eec65ccf6267173c11f3d14284d5f + languageName: node + linkType: hard + "@prisma/engines@npm:4.3.1": version: 4.3.1 resolution: "@prisma/engines@npm:4.3.1" @@ -3396,6 +3499,31 @@ __metadata: languageName: node linkType: hard +"@prisma/fetch-engine@npm:4.15.0": + version: 4.15.0 + resolution: "@prisma/fetch-engine@npm:4.15.0" + dependencies: + "@prisma/debug": 4.15.0 + "@prisma/get-platform": 4.15.0 + execa: 5.1.1 + find-cache-dir: 3.3.2 + fs-extra: 11.1.1 + hasha: 5.2.2 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + kleur: 4.1.5 + node-fetch: 2.6.11 + p-filter: 2.1.0 + p-map: 4.0.0 + p-retry: 4.6.2 + progress: 2.0.3 + rimraf: 3.0.2 + temp-dir: 2.0.0 + tempy: 1.0.1 + checksum: 4a34dcc30e0645ac0e03be746a609758f995520ce7c83c7a238dd379b349dd05198ac448f13f44f355ea95b34f044b3296603dfa5deb6ed12a86336cd7fe2c43 + languageName: node + linkType: hard + "@prisma/fetch-engine@npm:4.3.1": version: 4.3.1 resolution: "@prisma/fetch-engine@npm:4.3.1" @@ -3421,6 +3549,18 @@ __metadata: languageName: node linkType: hard +"@prisma/generator-helper@npm:4.15.0, @prisma/generator-helper@npm:^4.0.0, @prisma/generator-helper@npm:^4.7.1": + version: 4.15.0 + resolution: "@prisma/generator-helper@npm:4.15.0" + dependencies: + "@prisma/debug": 4.15.0 + "@types/cross-spawn": 6.0.2 + cross-spawn: 7.0.3 + kleur: 4.1.5 + checksum: 6e2f59448a1cfc48bc3687f8e557dff098e75440b018d665385c6b2af9fc7e05f7ffb8059eca4ce7c69148732a84cfbbef98921f5c0c58eb99bf5402b1e7c336 + languageName: node + linkType: hard + "@prisma/generator-helper@npm:4.3.1": version: 4.3.1 resolution: "@prisma/generator-helper@npm:4.3.1" @@ -3433,6 +3573,24 @@ __metadata: languageName: node linkType: hard +"@prisma/get-platform@npm:4.15.0": + version: 4.15.0 + resolution: "@prisma/get-platform@npm:4.15.0" + dependencies: + "@prisma/debug": 4.15.0 + escape-string-regexp: 4.0.0 + execa: 5.1.1 + fs-jetpack: 5.1.0 + kleur: 4.1.5 + replace-string: 3.1.0 + strip-ansi: 6.0.1 + tempy: 1.0.1 + terminal-link: 2.1.1 + ts-pattern: 4.3.0 + checksum: ec3c534db0f4376ff3c54e86869287e60b4b2a7280d92fe7e3f2281bac569f09b31bfd7cee9402f98b8beb52b52b2b1b0dc1455e649b8eb0ea6fd4d207ed46dc + languageName: node + linkType: hard + "@prisma/get-platform@npm:4.3.1": version: 4.3.1 resolution: "@prisma/get-platform@npm:4.3.1" @@ -3495,6 +3653,71 @@ __metadata: languageName: node linkType: hard +"@prisma/internals@npm:^4.0.0": + version: 4.15.0 + resolution: "@prisma/internals@npm:4.15.0" + dependencies: + "@opentelemetry/api": 1.4.1 + "@prisma/debug": 4.15.0 + "@prisma/engines": 4.15.0 + "@prisma/fetch-engine": 4.15.0 + "@prisma/generator-helper": 4.15.0 + "@prisma/get-platform": 4.15.0 + "@prisma/ni": 4.15.0 + "@prisma/prisma-fmt-wasm": 4.15.0-28.8fbc245156db7124f997f4cecdd8d1219e360944 + archiver: 5.3.1 + arg: 5.0.2 + checkpoint-client: 1.1.23 + cli-truncate: 2.1.0 + dotenv: 16.0.3 + escape-string-regexp: 4.0.0 + execa: 5.1.1 + find-up: 5.0.0 + fp-ts: 2.16.0 + fs-extra: 11.1.1 + fs-jetpack: 5.1.0 + global-dirs: 3.0.1 + globby: 11.1.0 + indent-string: 4.0.0 + is-windows: 1.0.2 + is-wsl: 2.2.0 + kleur: 4.1.5 + new-github-issue-url: 0.2.1 + node-fetch: 2.6.11 + npm-packlist: 5.1.3 + open: 7.4.2 + p-map: 4.0.0 + prompts: 2.4.2 + read-pkg-up: 7.0.1 + replace-string: 3.1.0 + resolve: 1.22.2 + string-width: 4.2.3 + strip-ansi: 6.0.1 + strip-indent: 3.0.0 + temp-dir: 2.0.0 + temp-write: 4.0.0 + tempy: 1.0.1 + terminal-link: 2.1.1 + tmp: 0.2.1 + ts-pattern: 4.3.0 + checksum: 012362d0d52c7a79274b923ea10bd9a582d9898fe1a01c259b0d32f284d8019251dedab4842b9eb2a4782ba131853a02941767ed166aef57b99db78d32bb1918 + languageName: node + linkType: hard + +"@prisma/ni@npm:4.15.0": + version: 4.15.0 + resolution: "@prisma/ni@npm:4.15.0" + checksum: 0736b77aa6b86abb5f2f8fa445dafbcdc94fe36ad61e85e5bcb90429fbec8d043f0ce83c44a57f79d426853cbbb16c7f660030fd882234b197bc70cdc1c7bda9 + languageName: node + linkType: hard + +"@prisma/prisma-fmt-wasm@npm:4.15.0-28.8fbc245156db7124f997f4cecdd8d1219e360944": + version: 4.15.0-28.8fbc245156db7124f997f4cecdd8d1219e360944 + resolution: "@prisma/prisma-fmt-wasm@npm:4.15.0-28.8fbc245156db7124f997f4cecdd8d1219e360944" + checksum: df990a57c4b40923062fd1abf0b2d776f4163efb3f8d89a4dbe2658401acb530c49fe7bb0d8d39e8627e45e3a317393aff21e36d07d3cf874cc15ce9ee260bbc + languageName: node + linkType: hard + "@prisma/prisma-fmt-wasm@npm:4.3.0-32.c875e43600dfe042452e0b868f7a48b817b9640b": version: 4.3.0-32.c875e43600dfe042452e0b868f7a48b817b9640b resolution: "@prisma/prisma-fmt-wasm@npm:4.3.0-32.c875e43600dfe042452e0b868f7a48b817b9640b" @@ -5408,6 +5631,18 @@ __metadata: languageName: node linkType: hard +"@ts-morph/common@npm:~0.17.0": + version: 0.17.0 + resolution: "@ts-morph/common@npm:0.17.0" + dependencies: + fast-glob: ^3.2.11 + minimatch: ^5.1.0 + mkdirp: ^1.0.4 + path-browserify: ^1.0.1 + checksum: 91b5f68b92d4e92376cd937e9a47b2bfce72d929bd2b15dbde4e3d3c7a81bfaeebfb11fe10477af5c8262f5a53deb3c1ee2bbc0b36a808dc04901c05330ef153 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.9 resolution: "@tsconfig/node10@npm:1.0.9" @@ -5548,6 +5783,13 @@ __metadata: languageName: node linkType: hard +"@types/bcryptjs@npm:^2.4.2": + version: 2.4.2 + resolution: "@types/bcryptjs@npm:2.4.2" + checksum: 92586439a25830c702978a4030ee84e6fd8d0d80a25171a274593b1637dc51c7087dadc44a1605819f1d10d2d0e4c686cae13f0fa5a3bbf91608d523861521a4 + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.2 resolution: "@types/body-parser@npm:1.19.2" @@ -5611,6 +5853,15 @@ __metadata: languageName: node linkType: hard +"@types/debug@npm:4.1.8": + version: 4.1.8 + resolution: "@types/debug@npm:4.1.8" + dependencies: + "@types/ms": "*" + checksum: 913aea60b8c94cd0009bbdd531d8a3594ec3275ca0e8d1cbcf783417884252b3c53113f6665fd2fb0076b8ce628ee12cd083d2af107ed26c0f2e75852d8bc074 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.3": version: 3.7.3 resolution: "@types/eslint-scope@npm:3.7.3" @@ -6742,6 +6993,51 @@ __metadata: languageName: node linkType: hard +"@zenstackhq/language@npm:1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "@zenstackhq/language@npm:1.0.0-beta.2" + dependencies: + langium: 1.2.0 + checksum: c40d14e6d20c6911484dbfc114a9d0a56355f5ea4295effef99520fd8dd1df414f5fba611efc1b99973f2aa70ffb4bda40ff3078c5b2bce77054f79edbc4624e + languageName: node + linkType: hard + +"@zenstackhq/runtime@npm:1.0.0-beta.2, @zenstackhq/runtime@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "@zenstackhq/runtime@npm:1.0.0-beta.2" + dependencies: + "@paralleldrive/cuid2": ^2.2.0 + "@types/bcryptjs": ^2.4.2 + bcryptjs: ^2.4.3 + change-case: ^4.1.2 + colors: 1.4.0 + decimal.js: ^10.4.2 + deepcopy: ^2.1.0 + lower-case-first: ^2.0.2 + pluralize: ^8.0.0 + superjson: ^1.11.0 + tslib: ^2.4.1 + zod: 3.21.1 + zod-validation-error: ^0.2.1 + peerDependencies: + "@prisma/client": ^4.0.0 + checksum: 52416703a0f1a208b88c8873deacfc4f9a18ebaf7820e93725d94d6eda3deebe33eb4d95081bbbbafaf44ec74c9edee6dc52b8c95667988863daa80a36749295 + languageName: node + linkType: hard + +"@zenstackhq/sdk@npm:1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "@zenstackhq/sdk@npm:1.0.0-beta.2" + dependencies: + "@prisma/generator-helper": ^4.7.1 + "@zenstackhq/language": 1.0.0-beta.2 + "@zenstackhq/runtime": 1.0.0-beta.2 + prettier: ^2.8.3 + ts-morph: ^16.0.0 + checksum: fa609f9f3f0c0dd4743b54861916188d415601582ee2adb11006eedd20b1c9b1de4f4e3c8b1449ff24bc4f721b8055465d614318baae4edfcf91327a86bf902e + languageName: node + linkType: hard + "@zxing/text-encoding@npm:0.9.0": version: 0.9.0 resolution: "@zxing/text-encoding@npm:0.9.0" @@ -7144,6 +7440,8 @@ __metadata: dependencies: "@redwoodjs/api": 3.2.0 "@redwoodjs/graphql-server": 3.2.0 + "@zenstackhq/runtime": ^1.0.0-beta.2 + zenstack: ^1.0.0-beta.2 languageName: unknown linkType: soft @@ -7503,6 +7801,13 @@ __metadata: languageName: node linkType: hard +"async-exit-hook@npm:^2.0.1": + version: 2.0.1 + resolution: "async-exit-hook@npm:2.0.1" + checksum: 81407a440ef0aab328df2369f1a9d957ee53e9a5a43e3b3dcb2be05151a68de0e4ff5e927f4718c88abf85800731f5b3f69a47a6642ce135f5e7d43ca0fce41d + languageName: node + linkType: hard + "async@npm:^3.2.3": version: 3.2.3 resolution: "async@npm:3.2.3" @@ -8019,6 +8324,13 @@ __metadata: languageName: node linkType: hard +"bcryptjs@npm:^2.4.3": + version: 2.4.3 + resolution: "bcryptjs@npm:2.4.3" + checksum: b969467087ed7a01ff905a1c6a0c45014ec586248a448ea08370c8ed8bb314bda16a870ca23e0961d7d23bdce1a04c76fa70a9d680be814fa9ac7d8fc61870a3 + languageName: node + linkType: hard + "better-opn@npm:^2.1.1": version: 2.1.1 resolution: "better-opn@npm:2.1.1" @@ -8805,6 +9117,21 @@ __metadata: languageName: node linkType: hard +"checkpoint-client@npm:1.1.23": + version: 1.1.23 + resolution: "checkpoint-client@npm:1.1.23" + dependencies: + ci-info: 3.3.0 + env-paths: 2.2.1 + fast-write-atomic: 0.2.1 + make-dir: 3.1.0 + ms: 2.1.3 + node-fetch: 2.6.7 + uuid: 8.3.2 + checksum: 1e0ec8bd3a6bc4bf9f53369eceb9c08d641143ba71f85caa6dca0982305b323d2711cda9cfb3f18d73fa6af0e4b794919bebe28a4d0d40d329276d91f11fa81a + languageName: node + linkType: hard + "cheerio-select@npm:^2.1.0": version: 2.1.0 resolution: "cheerio-select@npm:2.1.0" @@ -8834,6 +9161,31 @@ __metadata: languageName: node linkType: hard +"chevrotain-allstar@npm:~0.1.4": + version: 0.1.7 + resolution: "chevrotain-allstar@npm:0.1.7" + dependencies: + lodash: ^4.17.21 + peerDependencies: + chevrotain: ~10.4.1 + checksum: 536427e840971b134785cb647863358927f91ecf6fe323526b51a52dee4e9f7dfc6328fdc78035d129e7fcaec09660ef3beb3e6d8cb51b12bffba646ab4d9f1d + languageName: node + linkType: hard + +"chevrotain@npm:~10.4.2": + version: 10.4.2 + resolution: "chevrotain@npm:10.4.2" + 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 + checksum: 74b84498e666abfba75fa960035c5222083b81b6203b6b1a1dfb90ce1e55bca3a2057a9edc95b177ea397c96d9cbf6690b5332764fef2c0ffa912f9de5d57abc + languageName: node + linkType: hard + "chokidar@npm:3.5.3, chokidar@npm:^3.4.0, chokidar@npm:^3.4.1, chokidar@npm:^3.4.2, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" @@ -9090,6 +9442,13 @@ __metadata: languageName: node linkType: hard +"code-block-writer@npm:^11.0.3": + version: 11.0.3 + resolution: "code-block-writer@npm:11.0.3" + checksum: 12fe4c02152a2b607e8913b39dcc31dcb5240f7c8933a3335d4e42a5418af409bf7ed454c80d6d8c12f9c59bb685dd88f9467874b46be62236dfbed446d03fd6 + languageName: node + linkType: hard + "collapse-white-space@npm:^1.0.2": version: 1.0.6 resolution: "collapse-white-space@npm:1.0.6" @@ -9336,6 +9695,16 @@ __metadata: languageName: node linkType: hard +"config-chain@npm:^1.1.11": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: ^1.3.4 + proto-list: ~1.2.1 + checksum: 39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + "configstore@npm:3.1.5": version: 3.1.5 resolution: "configstore@npm:3.1.5" @@ -9435,6 +9804,15 @@ __metadata: languageName: node linkType: hard +"copy-anything@npm:^3.0.2": + version: 3.0.5 + resolution: "copy-anything@npm:3.0.5" + dependencies: + is-what: ^4.1.8 + checksum: 01eadd500c7e1db71d32d95a3bfaaedcb839ef891c741f6305ab0461398056133de08f2d1bf4c392b364e7bdb7ce498513896e137a7a183ac2516b065c28a4fe + languageName: node + linkType: hard + "copy-concurrently@npm:^1.0.0": version: 1.0.5 resolution: "copy-concurrently@npm:1.0.5" @@ -10146,6 +10524,13 @@ __metadata: languageName: node linkType: hard +"decimal.js@npm:^10.4.2": + version: 10.4.3 + resolution: "decimal.js@npm:10.4.3" + checksum: 6d60206689ff0911f0ce968d40f163304a6c1bc739927758e6efc7921cfa630130388966f16bf6ef6b838cb33679fbe8e7a78a2f3c478afce841fd55ac8fb8ee + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.0": version: 0.2.0 resolution: "decode-uri-component@npm:0.2.0" @@ -10162,6 +10547,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^7.0.0": + version: 7.0.0 + resolution: "decompress-response@npm:7.0.0" + dependencies: + mimic-response: ^3.1.0 + checksum: 925d5a34c9ae2c5026748feff4cbf1310b044be31618b188f1fe9f4ddfda4e013ebeeb0a7ec6d251095542ed3b3b879dac168e54cc65e5a82fb88ea0b287a788 + languageName: node + linkType: hard + "dedent@npm:^0.7.0": version: 0.7.0 resolution: "dedent@npm:0.7.0" @@ -10183,6 +10577,15 @@ __metadata: languageName: node linkType: hard +"deepcopy@npm:^2.1.0": + version: 2.1.0 + resolution: "deepcopy@npm:2.1.0" + dependencies: + type-detect: ^4.0.8 + checksum: d3bb7edbfc107559db60b38037e160a5ea6f83756c0b96ad111267d6a16a48d473e244ad8a03d0d1444142eb83306c40710f193e3fed24fbde4357c92e3d62c8 + languageName: node + linkType: hard + "deepmerge@npm:4.2.2, deepmerge@npm:^4.2.2": version: 4.2.2 resolution: "deepmerge@npm:4.2.2" @@ -10688,6 +11091,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:16.0.3": + version: 16.0.3 + resolution: "dotenv@npm:16.0.3" + checksum: 109457ac5f9e930ca8066ea33887b6f839ab24d647a7a8b49ddcd1f32662e2c35591c5e5b9819063e430148a664d0927f0cbe60cf9575d89bc524f47ff7e78f0 + languageName: node + linkType: hard + "dotenv@npm:^14.0.0": version: 14.3.2 resolution: "dotenv@npm:14.3.2" @@ -12357,6 +12767,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.2": + version: 1.15.2 + resolution: "follow-redirects@npm:1.15.2" + peerDependenciesMeta: + debug: + optional: true + checksum: da5932b70e63944d38eecaa16954bac4347036f08303c913d166eda74809d8797d38386e3a0eb1d2fe37d2aaff2764cce8e9dbd99459d860cf2cdfa237923b5f + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -12482,6 +12902,13 @@ __metadata: languageName: node linkType: hard +"fp-ts@npm:2.16.0": + version: 2.16.0 + resolution: "fp-ts@npm:2.16.0" + checksum: 288f84156dc0a53738778935c2aaf3d50878589c6130774a3cbefee10874b474b00b63e4c4738446afbdf8083c1f45f3229ebd111c91f959d73c812f3a53fd51 + languageName: node + linkType: hard + "fraction.js@npm:^4.2.0": version: 4.2.0 resolution: "fraction.js@npm:4.2.0" @@ -12505,7 +12932,7 @@ __metadata: languageName: node linkType: hard -"from2@npm:^2.1.0": +"from2@npm:^2.1.0, from2@npm:^2.3.0": version: 2.3.0 resolution: "from2@npm:2.3.0" dependencies: @@ -12533,6 +12960,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:11.1.1": + version: 11.1.1 + resolution: "fs-extra@npm:11.1.1" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: a2480243d7dcfa7d723c5f5b24cf4eba02a6ccece208f1524a2fbde1c629492cfb9a59e4b6d04faff6fbdf71db9fdc8ef7f396417a02884195a625f5d8dc9427 + languageName: node + linkType: hard + "fs-extra@npm:^0.30.0": version: 0.30.0 resolution: "fs-extra@npm:0.30.0" @@ -12568,6 +13006,15 @@ __metadata: languageName: node linkType: hard +"fs-jetpack@npm:5.1.0": + version: 5.1.0 + resolution: "fs-jetpack@npm:5.1.0" + dependencies: + minimatch: ^5.1.0 + checksum: e4961131bebc9c39b23f1c9d4e19c2d6228ed918a6b12749f239829b35748fcd5f7a6f1f201f061cd9720058f4b41138159e9650d56f675e5741426b75b260e0 + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -12741,6 +13188,36 @@ __metadata: languageName: node linkType: hard +"get-it@npm:^8.0.9": + version: 8.1.3 + resolution: "get-it@npm:8.1.3" + dependencies: + debug: ^4.3.4 + decompress-response: ^7.0.0 + follow-redirects: ^1.15.2 + into-stream: ^6.0.0 + is-plain-object: ^5.0.0 + is-retry-allowed: ^2.2.0 + is-stream: ^2.0.1 + parse-headers: ^2.0.5 + progress-stream: ^2.0.0 + tunnel-agent: ^0.6.0 + checksum: b63456ddd1aa32ea843e260bebc9790f13a8e3bc215e9cfaaa6a60225e49687e686b5541562f5ae78b53e83f320bdcb0f262e366c09f4d468da57bfb4b1a9c65 + languageName: node + linkType: hard + +"get-latest-version@npm:^5.0.1": + version: 5.0.1 + resolution: "get-latest-version@npm:5.0.1" + dependencies: + get-it: ^8.0.9 + registry-auth-token: ^5.0.2 + registry-url: ^5.1.0 + semver: ^7.3.8 + checksum: 27d193ff0cd7a6d3d586a8f4c868ed7568f64be43fe67cac3541a4ce18e2fd5e2c079c0d58c84832922b7baae5036b25b6bde03fa2107b7473d7f5d9ab34737d + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -12910,6 +13387,15 @@ __metadata: languageName: node linkType: hard +"global-dirs@npm:3.0.1": + version: 3.0.1 + resolution: "global-dirs@npm:3.0.1" + dependencies: + ini: 2.0.0 + checksum: ef65e2241a47ff978f7006a641302bc7f4c03dfb98783d42bf7224c136e3a06df046e70ee3a010cf30214114755e46c9eb5eb1513838812fbbe0d92b14c25080 + languageName: node + linkType: hard + "global-modules@npm:^1.0.0": version: 1.0.0 resolution: "global-modules@npm:1.0.0" @@ -13040,6 +13526,13 @@ __metadata: languageName: node linkType: hard +"graceful-fs@npm:4.2.10": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 4223a833e38e1d0d2aea630c2433cfb94ddc07dfc11d511dbd6be1d16688c5be848acc31f9a5d0d0ddbfb56d2ee5a6ae0278aceeb0ca6a13f27e06b9956fb952 + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.9 resolution: "graceful-fs@npm:4.2.9" @@ -13710,6 +14203,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:5.0.0": + version: 5.0.0 + resolution: "https-proxy-agent@npm:5.0.0" + dependencies: + agent-base: 6 + debug: 4 + checksum: 670c04f7f0effb5a449c094ea037cbcfb28a5ab93ed22e8c343095202cc7288027869a5a21caf4ee3b8ea06f9624ef1e1fc9044669c0fd92617654ff39f30806 + languageName: node + linkType: hard + "https-proxy-agent@npm:5.0.1, https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" @@ -13802,6 +14305,15 @@ __metadata: languageName: node linkType: hard +"ignore-walk@npm:^5.0.1": + version: 5.0.1 + resolution: "ignore-walk@npm:5.0.1" + dependencies: + minimatch: ^5.0.1 + checksum: 0d157a54d6d11af0c3059fdc7679eef3b074e9a663d110a76c72788e2fb5b22087e08b21ab767718187ac3396aca4d0aa6c6473f925b19a74d9a00480ca7a76e + languageName: node + linkType: hard + "ignore@npm:^4.0.3": version: 4.0.6 resolution: "ignore@npm:4.0.6" @@ -13974,6 +14486,16 @@ __metadata: languageName: node linkType: hard +"into-stream@npm:^6.0.0": + version: 6.0.0 + resolution: "into-stream@npm:6.0.0" + dependencies: + from2: ^2.3.0 + p-is-promise: ^3.0.0 + checksum: 576319a540d0e494f5f6028db364b0e163d58020139d862e5372c51ac35875e4ac2ee49fd821bb9225642de6add2e26dff82e5c41108d638a95930fa83bad750 + languageName: node + linkType: hard + "invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -14160,6 +14682,15 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.11.0": + version: 2.12.1 + resolution: "is-core-module@npm:2.12.1" + dependencies: + has: ^1.0.3 + checksum: ff1d0dfc0b7851310d289398e416eb92ae8a9ac7ea8b8b9737fa8c0725f5a78c5f3db6edd4dff38c9ed731f3aaa1f6410a320233fcb52a2c8f1cf58eebf10a4b + languageName: node + linkType: hard + "is-core-module@npm:^2.2.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": version: 2.10.0 resolution: "is-core-module@npm:2.10.0" @@ -14440,7 +14971,7 @@ __metadata: languageName: node linkType: hard -"is-plain-object@npm:5.0.0": +"is-plain-object@npm:5.0.0, is-plain-object@npm:^5.0.0": version: 5.0.0 resolution: "is-plain-object@npm:5.0.0" checksum: 893e42bad832aae3511c71fd61c0bf61aa3a6d853061c62a307261842727d0d25f761ce9379f7ba7226d6179db2a3157efa918e7fe26360f3bf0842d9f28942c @@ -14482,6 +15013,13 @@ __metadata: languageName: node linkType: hard +"is-retry-allowed@npm:^2.2.0": + version: 2.2.0 + resolution: "is-retry-allowed@npm:2.2.0" + checksum: 013be4f8a0a06a49ed1fe495242952e898325d496202a018f6f9fb3fb9ac8fe3b957a9bd62463d68299ae35dbbda680473c85a9bcefca731b49d500d3ccc08ff + languageName: node + linkType: hard + "is-set@npm:^2.0.2": version: 2.0.2 resolution: "is-set@npm:2.0.2" @@ -14505,7 +15043,7 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^2.0.0": +"is-stream@npm:^2.0.0, is-stream@npm:^2.0.1": version: 2.0.1 resolution: "is-stream@npm:2.0.1" checksum: 7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 @@ -14591,6 +15129,13 @@ __metadata: languageName: node linkType: hard +"is-what@npm:^4.1.8": + version: 4.1.15 + resolution: "is-what@npm:4.1.15" + checksum: 7d9bab85977d8352684a7b046cfee8d68e23029f0d6d5b4b7f366cf6c83dee39903e412b655ebf155dc9706d4d1bce02f6351f75a1426381961b4155394082db + languageName: node + linkType: hard + "is-whitespace-character@npm:^1.0.0": version: 1.0.4 resolution: "is-whitespace-character@npm:1.0.4" @@ -14605,7 +15150,7 @@ __metadata: languageName: node linkType: hard -"is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": +"is-windows@npm:1.0.2, is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": version: 1.0.2 resolution: "is-windows@npm:1.0.2" checksum: b32f418ab3385604a66f1b7a3ce39d25e8881dee0bd30816dc8344ef6ff9df473a732bcc1ec4e84fe99b2f229ae474f7133e8e93f9241686cfcf7eebe53ba7a5 @@ -14619,14 +15164,7 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^1.1.0": - version: 1.1.0 - resolution: "is-wsl@npm:1.1.0" - checksum: 7ad0012f21092d6f586c7faad84755a8ef0da9b9ec295e4dc82313cce4e1a93a3da3c217265016461f9b141503fe55fa6eb1fd5457d3f05e8d1bdbb48e50c13a - languageName: node - linkType: hard - -"is-wsl@npm:^2.1.1, is-wsl@npm:^2.2.0": +"is-wsl@npm:2.2.0, is-wsl@npm:^2.1.1, is-wsl@npm:^2.2.0": version: 2.2.0 resolution: "is-wsl@npm:2.2.0" dependencies: @@ -14635,6 +15173,13 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^1.1.0": + version: 1.1.0 + resolution: "is-wsl@npm:1.1.0" + checksum: 7ad0012f21092d6f586c7faad84755a8ef0da9b9ec295e4dc82313cce4e1a93a3da3c217265016461f9b141503fe55fa6eb1fd5457d3f05e8d1bdbb48e50c13a + languageName: node + linkType: hard + "isarray@npm:1.0.0, isarray@npm:^1.0.0, isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" @@ -15704,6 +16249,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:4.1.5": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a + languageName: node + linkType: hard + "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -15718,6 +16270,19 @@ __metadata: languageName: node linkType: hard +"langium@npm:1.2.0": + version: 1.2.0 + resolution: "langium@npm:1.2.0" + dependencies: + chevrotain: ~10.4.2 + chevrotain-allstar: ~0.1.4 + vscode-languageserver: ~8.0.2 + vscode-languageserver-textdocument: ~1.0.8 + vscode-uri: ~3.0.7 + checksum: 4bd3a316df85178b3bcae50484d53d447506d05025a0901358f507edb306b492937ce4891837ef16f3231e3dea7122c39f0067c6981d484147b38ee65b2a94f7 + languageName: node + linkType: hard + "language-subtag-registry@npm:~0.3.2": version: 0.3.21 resolution: "language-subtag-registry@npm:0.3.21" @@ -16705,6 +17270,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 + languageName: node + linkType: hard + "min-document@npm:^2.19.0": version: 2.19.0 resolution: "min-document@npm:2.19.0" @@ -16887,6 +17459,15 @@ __metadata: languageName: node linkType: hard +"mixpanel@npm:^0.17.0": + version: 0.17.0 + resolution: "mixpanel@npm:0.17.0" + dependencies: + https-proxy-agent: 5.0.0 + checksum: 0c16080948148e84dc7e4834eb3d83e530f24ae39395f73bbe7e99dedc900aa9251dea7b99522da067303726f46eebae7501b10a8f12efd3304667df3fc04d7c + languageName: node + linkType: hard + "mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3": version: 0.5.5 resolution: "mkdirp@npm:0.5.5" @@ -17145,6 +17726,20 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:2.6.11": + version: 2.6.11 + resolution: "node-fetch@npm:2.6.11" + dependencies: + whatwg-url: ^5.0.0 + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 3ec847ca43f678d07b80abfd85bdf06523c2554ee9a494c992c5fc61f5d9cde9f9f16aa33ff09a62f19eee9d54813b8850d7f054cdfee8b2daf789c57f8eeaea + languageName: node + linkType: hard + "node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" @@ -17224,6 +17819,13 @@ __metadata: languageName: node linkType: hard +"node-machine-id@npm:^1.1.12": + version: 1.1.12 + resolution: "node-machine-id@npm:1.1.12" + checksum: ab2fea5f75a6f1ce3c76c5e0ae3903b631230e0a99b003d176568fff8ddbdf7b2943be96cd8d220c497ca0f6149411831f8a450601929f326781cb1b59bab7f8 + languageName: node + linkType: hard + "node-releases@npm:^2.0.6": version: 2.0.6 resolution: "node-releases@npm:2.0.6" @@ -17322,6 +17924,36 @@ __metadata: languageName: node linkType: hard +"npm-bundled@npm:^2.0.0": + version: 2.0.1 + resolution: "npm-bundled@npm:2.0.1" + dependencies: + npm-normalize-package-bin: ^2.0.0 + checksum: 5b2dc1de455d38200e49c6205dee185ce919ea6b608672c693bec8907116bc5686dabcc150347630d351c1c533315fd60a1910ce00bdad6bb204cef016b90b7d + languageName: node + linkType: hard + +"npm-normalize-package-bin@npm:^2.0.0": + version: 2.0.0 + resolution: "npm-normalize-package-bin@npm:2.0.0" + checksum: 9b5283a2e423124c60fbc14244d36686b59e517d29156eacf9df8d3dc5d5bf4d9444b7669c607567ed2e089bbdbef5a2b3678cbf567284eeff3612da6939514b + languageName: node + linkType: hard + +"npm-packlist@npm:5.1.3": + version: 5.1.3 + resolution: "npm-packlist@npm:5.1.3" + dependencies: + glob: ^8.0.1 + ignore-walk: ^5.0.1 + npm-bundled: ^2.0.0 + npm-normalize-package-bin: ^2.0.0 + bin: + npm-packlist: bin/index.js + checksum: a8bea97661b2a7132bc8832d5560da24f823ee5324429bd16eb82b7873557de14641bc3fed8a7611b0d88b9771e59e99e01a9e551a53adb164327ded6128aada + languageName: node + linkType: hard + "npm-run-path@npm:^2.0.0": version: 2.0.2 resolution: "npm-run-path@npm:2.0.2" @@ -17593,7 +18225,7 @@ __metadata: languageName: node linkType: hard -"open@npm:7, open@npm:^7.0.3": +"open@npm:7, open@npm:7.4.2, open@npm:^7.0.3": version: 7.4.2 resolution: "open@npm:7.4.2" dependencies: @@ -17754,6 +18386,13 @@ __metadata: languageName: node linkType: hard +"p-is-promise@npm:^3.0.0": + version: 3.0.0 + resolution: "p-is-promise@npm:3.0.0" + checksum: 17a52c7a59a31a435a4721a7110faeccb7cc9179cf9cd00016b7a9a7156e2c2ed9d8e2efc0142acab74d5064fbb443eaeaf67517cf3668f2a7c93a7effad5bb9 + languageName: node + linkType: hard + "p-limit@npm:3.1.0, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -17962,6 +18601,13 @@ __metadata: languageName: node linkType: hard +"parse-headers@npm:^2.0.5": + version: 2.0.5 + resolution: "parse-headers@npm:2.0.5" + checksum: 950d75034f46be8b77c491754aefa61b32954e675200d9247ec60b2acaf85c0cc053c44e44b35feed9034a34cc696a5b6fda693b5a0b23daf3294959dd216124 + languageName: node + linkType: hard + "parse-json@npm:^2.2.0": version: 2.2.0 resolution: "parse-json@npm:2.2.0" @@ -18345,7 +18991,7 @@ __metadata: languageName: node linkType: hard -"pluralize@npm:8.0.0": +"pluralize@npm:8.0.0, pluralize@npm:^8.0.0": version: 8.0.0 resolution: "pluralize@npm:8.0.0" checksum: 2044cfc34b2e8c88b73379ea4a36fc577db04f651c2909041b054c981cd863dd5373ebd030123ab058d194ae615d3a97cfdac653991e499d10caf592e8b3dc33 @@ -18932,6 +19578,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^2.8.3": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" + bin: + prettier: bin-prettier.js + checksum: 463ea8f9a0946cd5b828d8cf27bd8b567345cf02f56562d5ecde198b91f47a76b7ac9eae0facd247ace70e927143af6135e8cf411986b8cb8478784a4d6d724a + languageName: node + linkType: hard + "pretty-bytes@npm:5.6.0": version: 5.6.0 resolution: "pretty-bytes@npm:5.6.0" @@ -19046,6 +19701,16 @@ __metadata: languageName: node linkType: hard +"progress-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "progress-stream@npm:2.0.0" + dependencies: + speedometer: ~1.0.0 + through2: ~2.0.3 + checksum: 25902a05d05932a49879bfb87bc1a5f6ea80d1174e1ed00c9fa6d28d22b8628c6d7fbc575ec3a552c070352158b8aa67d5584562d4c7032ccc706596f52e537d + languageName: node + linkType: hard + "progress@npm:2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" @@ -19104,6 +19769,15 @@ __metadata: languageName: node linkType: hard +"promisify@npm:^0.0.3": + version: 0.0.3 + resolution: "promisify@npm:0.0.3" + dependencies: + when: "" + checksum: 9d57cf3195c30a4d6e6286a7e99d1db6ad0a2f25ebc8b2e7fd82f4629720016071a40ff3eea205fb6b7e8326767656f02912fdb467a7414213972f46e65cd7e2 + languageName: node + linkType: hard + "prompts@npm:2.4.2, prompts@npm:^2.0.1, prompts@npm:^2.4.0": version: 2.4.2 resolution: "prompts@npm:2.4.2" @@ -19134,6 +19808,13 @@ __metadata: languageName: node linkType: hard +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + "proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -19773,6 +20454,13 @@ __metadata: languageName: node linkType: hard +"regexp-to-ast@npm:0.5.0": + version: 0.5.0 + resolution: "regexp-to-ast@npm:0.5.0" + checksum: 16d3c3905fb24866c3bff689ab177c1e63a7283a3cd1ba95987ef86020526f9827f5c60794197311f0e8a967889131142fe7a2e5ed3523ffe2ac9f55052e1566 + languageName: node + linkType: hard + "regexp.prototype.flags@npm:^1.4.1, regexp.prototype.flags@npm:^1.4.3": version: 1.4.3 resolution: "regexp.prototype.flags@npm:1.4.3" @@ -19814,7 +20502,16 @@ __metadata: languageName: node linkType: hard -"registry-url@npm:^5.0.0": +"registry-auth-token@npm:^5.0.2": + version: 5.0.2 + resolution: "registry-auth-token@npm:5.0.2" + dependencies: + "@pnpm/npm-conf": ^2.1.0 + checksum: 20fc2225681cc54ae7304b31ebad5a708063b1949593f02dfe5fb402bc1fc28890cecec6497ea396ba86d6cca8a8480715926dfef8cf1f2f11e6f6cc0a1b4bde + languageName: node + linkType: hard + +"registry-url@npm:^5.0.0, registry-url@npm:^5.1.0": version: 5.1.0 resolution: "registry-url@npm:5.1.0" dependencies: @@ -20118,6 +20815,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:1.22.2": + version: 1.22.2 + resolution: "resolve@npm:1.22.2" + dependencies: + is-core-module: ^2.11.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: f9f424a8117d1c68371b4fbc64e6ac045115a3beacc4bd3617b751f7624b69ad40c47dc995585c7f13d4a09723a8f167847defb7d39fad70b0d43bbba05ff851 + languageName: node + linkType: hard + "resolve@npm:^2.0.0-next.3": version: 2.0.0-next.3 resolution: "resolve@npm:2.0.0-next.3" @@ -20141,6 +20851,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@1.22.2#~builtin": + version: 1.22.2 + resolution: "resolve@patch:resolve@npm%3A1.22.2#~builtin::version=1.22.2&hash=07638b" + dependencies: + is-core-module: ^2.11.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: dcf068c4391941734efda06b6f778c013fd349cd4340f126de17c265a7b006c67de7e80e7aa06ecd29f3922e49f5561622b9faf98531f16aa9a896d22148c661 + languageName: node + linkType: hard + "resolve@patch:resolve@^2.0.0-next.3#~builtin": version: 2.0.0-next.3 resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin::version=2.0.0-next.3&hash=07638b" @@ -20529,6 +21252,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.8": + version: 7.5.2 + resolution: "semver@npm:7.5.2" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: d151207ab762a8067f6302076edc04e5b8da2362eb9e3f21c2567ceadfd415064936d215b4aae7791da118c230649d29089be979ffa49c5b56a6bcf82147efdd + languageName: node + linkType: hard + "semver@npm:~7.0.0": version: 7.0.0 resolution: "semver@npm:7.0.0" @@ -20852,6 +21586,13 @@ __metadata: languageName: node linkType: hard +"sleep-promise@npm:^9.1.0": + version: 9.1.0 + resolution: "sleep-promise@npm:9.1.0" + checksum: 15813898a356d64818d34b437523fe64b0804694730d23201af171e7f3172843d8fcbdc1ba7eea0a0f5b76afa94277a64ef829f22abe3801e3eb2e3915092aa3 + languageName: node + linkType: hard + "slice-ansi@npm:^3.0.0": version: 3.0.0 resolution: "slice-ansi@npm:3.0.0" @@ -21118,6 +21859,13 @@ __metadata: languageName: node linkType: hard +"speedometer@npm:~1.0.0": + version: 1.0.0 + resolution: "speedometer@npm:1.0.0" + checksum: d5bfafd5721a1d5300be03f1f3dead419fcec715103dcfd72a1cb85d64a7fae1afaf7e5c80b974a44a4550eafa01ae034e0dbddb50be1dba3a58c9241ee8de54 + languageName: node + linkType: hard + "split-string@npm:^3.0.1, split-string@npm:^3.0.2": version: 3.1.0 resolution: "split-string@npm:3.1.0" @@ -21470,6 +22218,13 @@ __metadata: languageName: node linkType: hard +"strip-color@npm:^0.1.0": + version: 0.1.0 + resolution: "strip-color@npm:0.1.0" + checksum: 24df29b62eefc57a1d7f35c36f3b0c070289a0742aa54af2ff9ee39eadb19e372588a6faa2fa6a98870d97cb70c5d77826de69fff5e383fc2c2bdbf24cf1bcc8 + languageName: node + linkType: hard + "strip-eof@npm:^1.0.0": version: 1.0.0 resolution: "strip-eof@npm:1.0.0" @@ -21572,6 +22327,15 @@ __metadata: languageName: node linkType: hard +"superjson@npm:^1.11.0": + version: 1.12.3 + resolution: "superjson@npm:1.12.3" + dependencies: + copy-anything: ^3.0.2 + checksum: 2f5ad70a7bd807fef1c43b7238eec093199545f8914c90f14feb7091f2aca1ed6ddda35c524eefedb9afade4c1fcdd2b4e527a2b897e14c7f7f0d6988c1c9dce + languageName: node + linkType: hard + "supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -21965,7 +22729,7 @@ __metadata: languageName: node linkType: hard -"through2@npm:^2.0.0, through2@npm:^2.0.1": +"through2@npm:^2.0.0, through2@npm:^2.0.1, through2@npm:~2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" dependencies: @@ -22250,6 +23014,16 @@ __metadata: languageName: node linkType: hard +"ts-morph@npm:^16.0.0": + version: 16.0.0 + resolution: "ts-morph@npm:16.0.0" + dependencies: + "@ts-morph/common": ~0.17.0 + code-block-writer: ^11.0.3 + checksum: 8ab316db96ec9254daf1f7193e225393c4a48b814f2d0191cde9da5b380a5a8d8eb3f75fb82b841335d06dd6e9c477daf0b8986e5b5b5e172064065b0c2b7408 + languageName: node + linkType: hard + "ts-node@npm:^10.8.1": version: 10.9.1 resolution: "ts-node@npm:10.9.1" @@ -22288,6 +23062,13 @@ __metadata: languageName: node linkType: hard +"ts-pattern@npm:4.3.0": + version: 4.3.0 + resolution: "ts-pattern@npm:4.3.0" + checksum: e83d370feeda3657f5b9d49b119840e4c4f7cbee9797233078cbf1ad8f82dbd44935e8120ee3293d7626fed542b230827fdb87fcbdcaf37fd0f1d22345d05594 + languageName: node + linkType: hard + "ts-pattern@npm:^4.0.1": version: 4.0.2 resolution: "ts-pattern@npm:4.0.2" @@ -22345,6 +23126,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.4.1": + version: 2.5.3 + resolution: "tslib@npm:2.5.3" + checksum: 4cb1817d34fae5b27d146e6c4a468d4155097d95c1335d0bc9690f11f33e63844806bf4ed6d97c30c72b8d85261b66cbbe16d871d9c594ac05701ec83e62a607 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -22363,6 +23151,15 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: ^5.0.1 + checksum: 4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -22381,7 +23178,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8": +"type-detect@npm:4.0.8, type-detect@npm:^4.0.8": version: 4.0.8 resolution: "type-detect@npm:4.0.8" checksum: 8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd @@ -22951,7 +23748,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:9.0.0": +"uuid@npm:9.0.0, uuid@npm:^9.0.0": version: 9.0.0 resolution: "uuid@npm:9.0.0" bin: @@ -23074,6 +23871,51 @@ __metadata: languageName: node linkType: hard +"vscode-jsonrpc@npm:8.0.2": + version: 8.0.2 + resolution: "vscode-jsonrpc@npm:8.0.2" + checksum: 349ae58c725d9d45f2f43914c0fdaf722a2f26669fb5f95f3200e1fd2520496936c20bee19144305dcbaa6ec2de438792444d3be365b110f454c377e37a70550 + languageName: node + linkType: hard + +"vscode-jsonrpc@npm:8.1.0, vscode-jsonrpc@npm:^8.0.2": + version: 8.1.0 + resolution: "vscode-jsonrpc@npm:8.1.0" + checksum: cb797b892227e7997378c6a1563b9849b6ec8190daae6fd18909fff4b33f6a98690bb315b95a3e4eeb9bf373d11896028c2111d71101544dfa16cb18a53cfcaa + languageName: node + linkType: hard + +"vscode-languageclient@npm:^8.0.2": + version: 8.1.0 + resolution: "vscode-languageclient@npm:8.1.0" + dependencies: + minimatch: ^5.1.0 + semver: ^7.3.7 + vscode-languageserver-protocol: 3.17.3 + checksum: dc141a91ec482289aa6e4510eb85bcca31aff8dab98fc369dd3edb45dc3e3b316ddc7618ea4f262a5c530cff21276b037e7b86cf408ee11ed8015b4268c5cba6 + languageName: node + linkType: hard + +"vscode-languageserver-protocol@npm:3.17.2": + version: 3.17.2 + resolution: "vscode-languageserver-protocol@npm:3.17.2" + dependencies: + vscode-jsonrpc: 8.0.2 + vscode-languageserver-types: 3.17.2 + checksum: 33b51bdbc69f0a3b680abc87d02372392afe139d092d47efc98dc716d1c4d71f6615f366698b563f88964554958e7097df5500c31ec739b21ea4e74b6d38254e + languageName: node + linkType: hard + +"vscode-languageserver-protocol@npm:3.17.3": + version: 3.17.3 + resolution: "vscode-languageserver-protocol@npm:3.17.3" + dependencies: + vscode-jsonrpc: 8.1.0 + vscode-languageserver-types: 3.17.3 + checksum: c8448cab8c8e3df386825f129a2f06a748510ad3407cb3d2122c6b661e973c32bb2f2612d64ea80e8c0344001adc80ee2f4c8bbce4fa81dd3eef888309457578 + languageName: node + linkType: hard + "vscode-languageserver-protocol@npm:^3.15.3": version: 3.16.0 resolution: "vscode-languageserver-protocol@npm:3.16.0" @@ -23091,6 +23933,13 @@ __metadata: languageName: node linkType: hard +"vscode-languageserver-textdocument@npm:^1.0.7, vscode-languageserver-textdocument@npm:~1.0.8": + version: 1.0.10 + resolution: "vscode-languageserver-textdocument@npm:1.0.10" + checksum: 0f4e82c262eaea2ed84e004c751453ac900a669763287dee92f9b631804a6ef1dabd62880185fa4d9a063f27d46e65c5251f7f0c8329aee5e3b3db6b6dfa2109 + languageName: node + linkType: hard + "vscode-languageserver-types@npm:3.16.0": version: 3.16.0 resolution: "vscode-languageserver-types@npm:3.16.0" @@ -23105,6 +23954,13 @@ __metadata: languageName: node linkType: hard +"vscode-languageserver-types@npm:3.17.3": + version: 3.17.3 + resolution: "vscode-languageserver-types@npm:3.17.3" + checksum: a70d4ac0dbc08ba425b97e329e6a5696dabc6a264415bacb861e10d859b224f1b46d1fb41c17b6fdd31b32749d3bdfc819cb1b8a57dbe0d1e70e661ba8cfa397 + languageName: node + linkType: hard + "vscode-languageserver@npm:6.1.1": version: 6.1.1 resolution: "vscode-languageserver@npm:6.1.1" @@ -23116,6 +23972,35 @@ __metadata: languageName: node linkType: hard +"vscode-languageserver@npm:^8.0.2": + version: 8.1.0 + resolution: "vscode-languageserver@npm:8.1.0" + dependencies: + vscode-languageserver-protocol: 3.17.3 + bin: + installServerIntoExtension: bin/installServerIntoExtension + checksum: 1c05a491bbb4fb5266b3cc7d0be3b267aea2ff0c8aece2d8fb35e0a1b86ccc9aee252d08becec97d292fd3e1cc099cf350d720fb6c1a19f64fd1ac360f6248ae + languageName: node + linkType: hard + +"vscode-languageserver@npm:~8.0.2": + version: 8.0.2 + resolution: "vscode-languageserver@npm:8.0.2" + dependencies: + vscode-languageserver-protocol: 3.17.2 + bin: + installServerIntoExtension: bin/installServerIntoExtension + checksum: 0dfe96f9c7ecee29ed3005cedde8a1055a6be2d56a28824328ea687d4df584227964888f55ec35d8d496db63869eda8c2782b3d2a97cf4685c9f7e24c20ef72e + languageName: node + linkType: hard + +"vscode-uri@npm:^3.0.6, vscode-uri@npm:~3.0.7": + version: 3.0.7 + resolution: "vscode-uri@npm:3.0.7" + checksum: 67bc15bc9c9bd2d70dae8b27f2a3164281c6ee8f6484e6c5945a44d89871da93d52f2ba339ebc12ab0c10991d4576171b5d85e49a542454329c16faf977e4c59 + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -23665,6 +24550,13 @@ __metadata: languageName: node linkType: hard +"when@npm:": + version: 3.7.8 + resolution: "when@npm:3.7.8" + checksum: de42359a40d93d00890be87e6955928c247c026572e5b9ab141e1c62369bfe2832770c1dab00429d5823bc63850d1432de0aa0be963e95f0ca03c03f9a76022d + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" @@ -24090,6 +24982,48 @@ __metadata: languageName: node linkType: hard +"zenstack@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "zenstack@npm:1.0.0-beta.2" + dependencies: + "@paralleldrive/cuid2": ^2.2.0 + "@prisma/generator-helper": ^4.0.0 + "@prisma/internals": ^4.0.0 + "@zenstackhq/language": 1.0.0-beta.2 + "@zenstackhq/sdk": 1.0.0-beta.2 + async-exit-hook: ^2.0.1 + change-case: ^4.1.2 + colors: 1.4.0 + commander: ^8.3.0 + get-latest-version: ^5.0.1 + langium: 1.2.0 + lower-case-first: ^2.0.2 + mixpanel: ^0.17.0 + node-machine-id: ^1.1.12 + ora: ^5.4.1 + pluralize: ^8.0.0 + promisify: ^0.0.3 + semver: ^7.3.8 + sleep-promise: ^9.1.0 + strip-color: ^0.1.0 + ts-morph: ^16.0.0 + upper-case-first: ^2.0.2 + 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.21.1 + zod-validation-error: ^0.2.1 + peerDependencies: + prisma: ^4.0.0 + bin: + zenstack: bin/cli + checksum: d87c804b36f8130609e56a14ea0e8b2e31594dfb4c8e71683cf19850f6054d5f4e6b02222605525793099d16cc7e05fa29920239af400cb44b108e92b424650b + languageName: node + linkType: hard + "zip-stream@npm:^4.1.0": version: 4.1.0 resolution: "zip-stream@npm:4.1.0" @@ -24101,6 +25035,22 @@ __metadata: languageName: node linkType: hard +"zod-validation-error@npm:^0.2.1": + version: 0.2.2 + resolution: "zod-validation-error@npm:0.2.2" + peerDependencies: + zod: ^3.18.0 + checksum: 8c2cab8fc38e2b6b69719453cf6ecc12b92bf0ef32d9736da9811294522df2373703e98754686be33bd9a5df10c2d229a4422d0567680b94e063e2d924ddd12d + languageName: node + linkType: hard + +"zod@npm:3.21.1": + version: 3.21.1 + resolution: "zod@npm:3.21.1" + checksum: f089f1b53657fcbfea0888b5b75db2973c619006f8817707369a9332a4916ec6784c2cdb9e3cc03432c8dc815ed3f9bc05631e15829104a1bb1e6feb82f20a20 + languageName: node + linkType: hard + "zwitch@npm:^1.0.0": version: 1.0.5 resolution: "zwitch@npm:1.0.5" From 4f6400f40baf9d1143c4b87288cd3cd06624ee32 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:03:59 +0800 Subject: [PATCH 2/8] update readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3c661055f..0649aea4f 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Follow these steps to get started with ZenStack: ```diff export const deleteComment = ({ id }) => { - - requireAuth({ roles: 'moderator' }) + - requireAuth({ roles: 'moderator' }) return authDb().comment.delete({ where: { id } }) } ``` @@ -115,10 +115,10 @@ Follow these steps to get started with ZenStack: ```diff type Mutation { - - createComment(input: CreateCommentInput!): Comment! @skipAuth - - deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator") - + createComment(input: CreateCommentInput!): Comment! @skipAuth - + deleteComment(id: Int!): Comment! @skipAuth + - createComment(input: CreateCommentInput!): Comment! @skipAuth + - deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator") + + createComment(input: CreateCommentInput!): Comment! @skipAuth + + deleteComment(id: Int!): Comment! @skipAuth } ``` From b9df9f6567c8b07157f6d9d8d250b2c9dbcdb351 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:11:08 +0800 Subject: [PATCH 3/8] update readme --- README.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0649aea4f..5fd6e0a29 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Follow these steps to get started with ZenStack: cp db/schema.prisma ./schema.zmodel ``` -2. Prepare model +1. Prepare model Add the following section to `schema.zmodel` to output the generated Prisma schema to the default location of Redwood: @@ -35,7 +35,7 @@ Follow these steps to get started with ZenStack: yarn zenstack generate ``` -3. Add access policies +1. Add access policies Note the added `@@allow` rules (all operations are denied by default). @@ -93,6 +93,8 @@ Follow these steps to get started with ZenStack: ``` + See the next section for where the `auth()` function's value comes from. + Rerun generation and migrate the database. ```bash @@ -100,7 +102,25 @@ Follow these steps to get started with ZenStack: yarn rw prisma migrate dev ``` -4. Switch to relying on access policies for authorization +1. Create access-policy-enhanced Prisma Client + + Add the following function to `api/src/lib/db.js`: + + ```js + import { withPolicy } from '@zenstackhq/runtime' + + /* + * Returns ZenStack wrapped Prisma Client with access policies enabled. + */ + export function authDb() { + console.log('Context User:', context.currentUser) + return withPolicy(db, { user: context.currentUser }) + } + ``` + + It uses the `withPolicy` API to create a Prisma Client wrapper (note the `context.currentUser` is passed in as the current user, which determines what the `auth()` function returns in the ZModel policy rules). + +1. Switch to relying on access policies for authorization Remove authorization from `api/src/services/comments.js` @@ -136,7 +156,7 @@ Follow these steps to get started with ZenStack: -)} ``` -5. Test it +1. Test it Now if you delete a comment with a moderator role, it should succeed. A failure will be generated for other roles. From 5ad45a4f1a61226f0d6c0d05479c581ef446b977 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:14:32 +0800 Subject: [PATCH 4/8] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5fd6e0a29..a5d619174 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ Follow these steps to get started with ZenStack: ```diff export const deleteComment = ({ id }) => { - requireAuth({ roles: 'moderator' }) + - db.comment.delete({ where: { id } }) return authDb().comment.delete({ where: { id } }) } ``` From d625299ef83deb140aa0c9e8650104e81bbe2594 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:14:55 +0800 Subject: [PATCH 5/8] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5d619174..fc5683aa4 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ Follow these steps to get started with ZenStack: ```diff export const deleteComment = ({ id }) => { - requireAuth({ roles: 'moderator' }) - - db.comment.delete({ where: { id } }) + - return db.comment.delete({ where: { id } }) return authDb().comment.delete({ where: { id } }) } ``` From 39c2039080df33e17f6cf2b7cca97a37707038fd Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Thu, 22 Jun 2023 19:15:01 +0800 Subject: [PATCH 6/8] update policies and UI --- .../migration.sql | 19 ++++++ api/db/schema.prisma | 13 ++-- api/schema.zmodel | 22 +++--- api/src/graphql/adminPosts.sdl.js | 2 + api/src/graphql/posts.sdl.js | 1 + api/src/services/adminPosts/adminPosts.js | 22 ++++++ api/src/services/comments/comments.js | 9 +-- api/src/services/posts/posts.js | 9 ++- web/src/components/Post/Posts/Posts.js | 68 ++++++++++++++++++- .../components/Post/PostsCell/PostsCell.js | 1 + 10 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 api/db/migrations/20230622084334_add_published/migration.sql diff --git a/api/db/migrations/20230622084334_add_published/migration.sql b/api/db/migrations/20230622084334_add_published/migration.sql new file mode 100644 index 000000000..5f6d98d82 --- /dev/null +++ b/api/db/migrations/20230622084334_add_published/migration.sql @@ -0,0 +1,19 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Post" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "published" BOOLEAN NOT NULL DEFAULT true, + "zenstack_guard" BOOLEAN NOT NULL DEFAULT true, + "zenstack_transaction" TEXT, + CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Post" ("body", "createdAt", "id", "title", "userId", "zenstack_guard", "zenstack_transaction") SELECT "body", "createdAt", "id", "title", "userId", "zenstack_guard", "zenstack_transaction" FROM "Post"; +DROP TABLE "Post"; +ALTER TABLE "new_Post" RENAME TO "Post"; +CREATE INDEX "Post_zenstack_transaction_idx" ON "Post"("zenstack_transaction"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/api/db/schema.prisma b/api/db/schema.prisma index a7d3000ff..0e176f067 100644 --- a/api/db/schema.prisma +++ b/api/db/schema.prisma @@ -11,11 +11,11 @@ datasource db { generator client { provider = "prisma-client-js" binaryTargets = "native" + previewFeatures = ["interactiveTransactions"] } -/// @@allow('read', true) -/// @@allow('all', auth() == user) -/// @@allow('all', auth() != null && auth().roles == 'admin') +/// @@allow('all', auth().roles == 'admin' && auth() == user) +/// @@allow('read', published) model Post { id Int @id() @default(autoincrement()) title String @@ -24,6 +24,7 @@ model Post { user User @relation(fields: [userId], references: [id]) userId Int createdAt DateTime @default(now()) + published Boolean @default(true) zenstack_guard Boolean @default(true) zenstack_transaction String? @@ -39,8 +40,6 @@ model Contact { createdAt DateTime @default(now()) } -/// @@allow('create,read', true) -/// @@allow('update,delete', auth() == this) model User { id Int @id() @default(autoincrement()) name String? @@ -58,8 +57,8 @@ model User { @@index([zenstack_transaction]) } -/// @@allow('read,create', true) -/// @@allow('delete', auth() != null && auth().roles == 'moderator') +/// @@allow('all', auth().roles == 'moderator') +/// @@allow('create,read', post.published) model Comment { id Int @id() @default(autoincrement()) name String diff --git a/api/schema.zmodel b/api/schema.zmodel index 6106b17a4..d8959267c 100644 --- a/api/schema.zmodel +++ b/api/schema.zmodel @@ -11,6 +11,7 @@ plugin prisma { generator client { provider = "prisma-client-js" binaryTargets = "native" + previewFeatures = ["interactiveTransactions"] } model Post { @@ -21,10 +22,13 @@ model Post { user User @relation(fields: [userId], references: [id]) userId Int createdAt DateTime @default(now()) + published Boolean @default(true) - @@allow('read', true) - @@allow('all', auth() == user) - @@allow('all', auth() != null && auth().roles == 'admin') + // 🔐 Admin user can do everything to his own posts + @@allow('all', auth().roles == 'admin' && auth() == user) + + // 🔐 Posts are visible to everyone if published + @@allow('read', published) } model Contact { @@ -33,8 +37,6 @@ model Contact { email String message String createdAt DateTime @default(now()) - - @@allow('create', true) } model User { @@ -47,9 +49,6 @@ model User { resetTokenExpiresAt DateTime? roles String @default("moderator") posts Post[] - - @@allow('create,read', true) - @@allow('update,delete', auth() == this) } model Comment { @@ -60,6 +59,9 @@ model Comment { postId Int createdAt DateTime @default(now()) - @@allow('read,create', true) - @@allow('delete', auth() != null && auth().roles == 'moderator') + // 🔐 Moderator user can do everything to comments + @@allow('all', auth().roles == 'moderator') + + // 🔐 Everyone is allowed to view and create comments for published posts + @@allow('create,read', post.published) } diff --git a/api/src/graphql/adminPosts.sdl.js b/api/src/graphql/adminPosts.sdl.js index c4b49a6e4..0a49626e2 100644 --- a/api/src/graphql/adminPosts.sdl.js +++ b/api/src/graphql/adminPosts.sdl.js @@ -18,6 +18,8 @@ export const schema = gql` createPost(input: CreatePostInput!): Post! @requireAuth(roles: ["admin"]) updatePost(id: Int!, input: UpdatePostInput!): Post! @requireAuth(roles: ["admin"]) + publishPost(id: Int!): Post! @requireAuth(roles: ["admin"]) + unpublishPost(id: Int!): Post! @requireAuth(roles: ["admin"]) deletePost(id: Int!): Post! @requireAuth(roles: ["admin"]) } ` diff --git a/api/src/graphql/posts.sdl.js b/api/src/graphql/posts.sdl.js index 95b30ddf4..ae78e0f82 100644 --- a/api/src/graphql/posts.sdl.js +++ b/api/src/graphql/posts.sdl.js @@ -5,6 +5,7 @@ export const schema = gql` body: String! createdAt: DateTime! user: User! + published: Boolean! } type Query { diff --git a/api/src/services/adminPosts/adminPosts.js b/api/src/services/adminPosts/adminPosts.js index d409905e9..897ef4605 100644 --- a/api/src/services/adminPosts/adminPosts.js +++ b/api/src/services/adminPosts/adminPosts.js @@ -35,6 +35,28 @@ export const updatePost = async ({ id, input }) => { }) } +export const publishPost = async ({ id }) => { + await validateOwnership({ id }) + + return db.post.update({ + where: { id }, + data: { + published: true, + }, + }) +} + +export const unpublishPost = async ({ id }) => { + await validateOwnership({ id }) + + return db.post.update({ + where: { id }, + data: { + published: false, + }, + }) +} + export const deletePost = async ({ id }) => { await validateOwnership({ id }) diff --git a/api/src/services/comments/comments.js b/api/src/services/comments/comments.js index 7a9ba5f8d..42345d118 100644 --- a/api/src/services/comments/comments.js +++ b/api/src/services/comments/comments.js @@ -17,11 +17,6 @@ export const createComment = ({ input }) => { } export const deleteComment = ({ id }) => { - // instead of checking roles explicitly, we now rely on the - // access policies to authorize the operation - // - // requireAuth({ roles: 'moderator' }) - // return authDb().comment.delete({ where: { id }, }) @@ -29,8 +24,6 @@ export const deleteComment = ({ id }) => { export const Comment = { post: (_obj, { root }) => { - return authDb() - .comment.findUnique({ where: { id: root?.id } }) - .post() + return authDb().post.findUnique({ where: { id: root?.postId } }) }, } diff --git a/api/src/services/posts/posts.js b/api/src/services/posts/posts.js index b41b1d610..d7a880fd7 100644 --- a/api/src/services/posts/posts.js +++ b/api/src/services/posts/posts.js @@ -1,16 +1,15 @@ -import { db } from 'src/lib/db' +import { authDb, db } from 'src/lib/db' export const posts = (...args) => { - return db.post.findMany() + return authDb().post.findMany() } export const post = ({ id }) => { - return db.post.findUnique({ + return authDb().post.findUnique({ where: { id }, }) } export const Post = { - user: (_obj, { root }) => - db.post.findFirst({ where: { id: root.id } }).user(), + user: (_obj, { root }) => db.user.findUnique({ where: { id: root.userId } }), } diff --git a/web/src/components/Post/Posts/Posts.js b/web/src/components/Post/Posts/Posts.js index 4c44edfaa..7ea34a27a 100644 --- a/web/src/components/Post/Posts/Posts.js +++ b/web/src/components/Post/Posts/Posts.js @@ -1,6 +1,6 @@ +import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { Link, routes } from '@redwoodjs/router' import { QUERY } from 'src/components/Post/PostsCell' @@ -12,6 +12,22 @@ const DELETE_POST_MUTATION = gql` } ` +const PUBLISH_POST_MUTATION = gql` + mutation PublishPostMutation($id: Int!) { + publishPost(id: $id) { + id + } + } +` + +const UNPUBLISH_POST_MUTATION = gql` + mutation UnpublishPostMutation($id: Int!) { + unpublishPost(id: $id) { + id + } + } +` + const MAX_STRING_LENGTH = 150 const truncate = (text) => { @@ -50,6 +66,36 @@ const PostsList = ({ posts }) => { awaitRefetchQueries: true, }) + const [publishPost] = useMutation(PUBLISH_POST_MUTATION, { + onCompleted: () => { + toast.success('Post published') + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const [unpublishPost] = useMutation(UNPUBLISH_POST_MUTATION, { + onCompleted: () => { + toast.success('Post unpublished') + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const onPublishClick = (id) => { + publishPost({ variables: { id } }) + } + + const onUnpublishClick = (id) => { + unpublishPost({ variables: { id } }) + } + const onDeleteClick = (id) => { if (confirm('Are you sure you want to delete post ' + id + '?')) { deletePost({ variables: { id } }) @@ -91,6 +137,26 @@ const PostsList = ({ posts }) => { > Edit + {post.published && ( + onUnpublishClick(post.id)} + > + Unpublish + + )} + {!post.published && ( + onPublishClick(post.id)} + > + Publish + + )} Date: Thu, 22 Jun 2023 21:12:19 +0800 Subject: [PATCH 7/8] update readme --- README.md | 162 +----------------------------------------------------- 1 file changed, 2 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index fc5683aa4..10a174eb5 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,6 @@ # Redwood Tutorial App -Follow these steps to get started with ZenStack: +This project demonstrates how to use [ZenStack](https://zenstack.dev) in a RedwoodJS project. It's extended based on the blog app used through [RedwoodJS's tutorial](https://redwoodjs.com/docs/tutorial/foreword). -1. Prepare project +Please refer to [this blog post](https://zenstack.dev/blog/redwood-auth) for a general introduction. - Install CLI - - ```bash - cd api - yarn add -D zenstack - yarn add @zenstackhq/runtime - ``` - - Bootstrap ZModel from `schema.prisma` - - ```bash - cp db/schema.prisma ./schema.zmodel - ``` - -1. Prepare model - - Add the following section to `schema.zmodel` to output the generated Prisma schema to the default location of Redwood: - - ``` - plugin prisma { - provider = '@core/prisma' - output = './db/schema.prisma' - } - ``` - - Run `zenstack generate` and verify that `db/schema.prisma` is updated. - - ```bash - yarn zenstack generate - ``` - -1. Add access policies - - Note the added `@@allow` rules (all operations are denied by default). - - ```prisma - model Post { - id Int @id @default(autoincrement()) - title String - body String - comments Comment[] - user User @relation(fields: [userId], references: [id]) - userId Int - createdAt DateTime @default(now()) - - @@allow('read', true) - @@allow('all', auth() == user) - @@allow('all', auth() != null && auth().roles == 'admin') - } - - model Contact { - id Int @id @default(autoincrement()) - name String - email String - message String - createdAt DateTime @default(now()) - - @@allow('create', true) - } - - model User { - id Int @id @default(autoincrement()) - name String? - email String @unique - hashedPassword String - salt String - resetToken String? - resetTokenExpiresAt DateTime? - roles String @default("moderator") - posts Post[] - - @@allow('create,read', true) - @@allow('update,delete', auth() == this) - } - - model Comment { - id Int @id @default(autoincrement()) - name String - body String - post Post @relation(fields: [postId], references: [id]) - postId Int - createdAt DateTime @default(now()) - - @@allow('read,create', true) - @@allow('delete', auth() != null && auth().roles == 'moderator') - } - - ``` - - See the next section for where the `auth()` function's value comes from. - - Rerun generation and migrate the database. - - ```bash - yarn zenstack generate - yarn rw prisma migrate dev - ``` - -1. Create access-policy-enhanced Prisma Client - - Add the following function to `api/src/lib/db.js`: - - ```js - import { withPolicy } from '@zenstackhq/runtime' - - /* - * Returns ZenStack wrapped Prisma Client with access policies enabled. - */ - export function authDb() { - console.log('Context User:', context.currentUser) - return withPolicy(db, { user: context.currentUser }) - } - ``` - - It uses the `withPolicy` API to create a Prisma Client wrapper (note the `context.currentUser` is passed in as the current user, which determines what the `auth()` function returns in the ZModel policy rules). - -1. Switch to relying on access policies for authorization - - Remove authorization from `api/src/services/comments.js` - - ```diff - export const deleteComment = ({ id }) => { - - requireAuth({ roles: 'moderator' }) - - return db.comment.delete({ where: { id } }) - return authDb().comment.delete({ where: { id } }) - } - ``` - - and `api/src/graphql/comments.sdl.js` - - ```diff - type Mutation { - - createComment(input: CreateCommentInput!): Comment! @skipAuth - - deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator") - + createComment(input: CreateCommentInput!): Comment! @skipAuth - + deleteComment(id: Int!): Comment! @skipAuth - } - ``` - - Change UI to allow everyone to delete comments: `web/src/components/Comment/Comment.js`. - - ```diff - - {hasRole('moderator') && ( - - -)} - ``` - -1. Test it - - Now if you delete a comment with a moderator role, it should succeed. A failure will be generated for other roles. - - The error thrown is of type `PrismaClientKnownRequestError`. It seems by default Redwood's GraphQL server captures it and returns a generic error message. Need to figure out how to set up a global error handler to turn it into a proper "Forbidden" error. From fbe01bfb4be0bcf3172387900b08b29b9a631939 Mon Sep 17 00:00:00 2001 From: Yiming Date: Fri, 23 Jun 2023 09:25:35 +0800 Subject: [PATCH 8/8] Update README.md --- README.md | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/README.md b/README.md index 10a174eb5..34b4b3ba1 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,116 @@ This project demonstrates how to use [ZenStack](https://zenstack.dev) in a Redwo Please refer to [this blog post](https://zenstack.dev/blog/redwood-auth) for a general introduction. +--- + +## Steps to get started with ZenStack + +1. Prepare project + + Install CLI + + ```bash + cd api + yarn add -D zenstack + yarn add @zenstackhq/runtime + ``` + + Bootstrap ZModel from `schema.prisma` + + ```bash + cp db/schema.prisma ./schema.zmodel + ``` + +1. Prepare model + + Add the following section to `schema.zmodel` to output the generated Prisma schema to the default location of Redwood: + + ``` + plugin prisma { + provider = '@core/prisma' + output = './db/schema.prisma' + } + ``` + + Run `zenstack generate` and verify that `db/schema.prisma` is updated. + + ```bash + yarn zenstack generate + ``` + +1. Add access policies + + Note the added `@@allow` rules (all operations are denied by default). + + ```prisma + model Post { + id Int @id @default(autoincrement()) + title String + body String + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + published Boolean @default(true) + + // 🔐 Admin user can do everything to his own posts + @@allow('all', auth().roles == 'admin' && auth() == user) + + // 🔐 Posts are visible to everyone if published + @@allow('read', published) + } + + model Comment { + id Int @id @default(autoincrement()) + name String + body String + post Post @relation(fields: [postId], references: [id]) + postId Int + createdAt DateTime @default(now()) + + // 🔐 Moderator user can do everything to comments + @@allow('all', auth().roles == 'moderator') + + // 🔐 Everyone is allowed to view and create comments for published posts + @@allow('create,read', post.published) + } + + ``` + + See the next section for where the `auth()` function's value comes from. + + Rerun generation and migrate the database. + + ```bash + yarn zenstack generate + yarn rw prisma migrate dev + ``` + +1. Create access-policy-enhanced Prisma Client + + Add the following function to `api/src/lib/db.js`: + + ```js + import { withPolicy } from '@zenstackhq/runtime' + + /* + * Returns ZenStack wrapped Prisma Client with access policies enabled. + */ + export function authDb() { + return withPolicy(db, { user: context.currentUser }) + } + ``` + + It uses the `withPolicy` API to create a Prisma Client wrapper (note the `context.currentUser` is passed in as the current user, which determines what the `auth()` function returns in the ZModel policy rules). + +1. Switch to relying on access policies for authorization + + For example, remove authorization from `api/src/services/comments.js` and use `authDb()` helper to access db. + + ```diff + export const deleteComment = ({ id }) => { + - requireAuth({ roles: 'moderator' }) + - return db.comment.delete({ where: { id } }) + return authDb().comment.delete({ where: { id } }) + } + ```