Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
63fa55e410
Bump node-fetch from 2.6.6 to 2.6.7
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.6 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.6...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-27 11:59:31 +00:00
72 changed files with 295 additions and 1309 deletions

View File

@ -8,13 +8,6 @@ on:
branches: branches:
- master - master
env:
CREATE_ADMIN: true
ADMIN_EMAIL: admin@localhost
ADMIN_USERNAME: admin
ADMIN_PASSWORD: admin
MAILER_URI: smtp://localhost:1025
jobs: jobs:
run-linters: run-linters:
name: Run linters name: Run linters
@ -42,136 +35,3 @@ jobs:
- name: Typecheck - name: Typecheck
uses: andoshin11/typescript-error-reporter-action@v1.0.2 uses: andoshin11/typescript-error-reporter-action@v1.0.2
run-postgres:
name: Run Postgres
runs-on: ubuntu-latest
services:
postgres:
image: postgres:10-alpine
env:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
POSTGRES_DB: ohmyform
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Node.js dependencies
run: yarn install --frozen-lockfile --silent
- name: PostgreSQL Migrations
run: yarn typeorm migration:run
env:
DATABASE_DRIVER: postgres
TYPEORM_CONNECTION: postgres
TYPEORM_HOST: localhost
TYPEORM_PORT: 5432
TYPEORM_USERNAME: root
TYPEORM_PASSWORD: root
TYPEORM_DATABASE: ohmyform
TYPEORM_AUTO_SCHEMA_SYNC: false
TYPEORM_ENTITIES: src/entity/**/*.ts
TYPEORM_SUBSCRIBERS: src/subscriber/**/*.ts
TYPEORM_MIGRATIONS: src/migrations/postgres/**/*.ts
TYPEORM_MIGRATIONS_TRANSACTION_MODE: 'each'
TYPEORM_ENTITIES_DIR: src/entity
TYPEORM_MIGRATIONS_DIR: src/migrations/postgres
TYPEORM_SUBSCRIBERS_DIR: src/subscriber
run-mariadb:
name: Run MariaDB
runs-on: ubuntu-latest
services:
mariadb:
image: mariadb
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: ohmyform
ports:
- 3306:3306
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Node.js dependencies
run: yarn install --frozen-lockfile --silent
- name: MariaDB Migrations
run: yarn typeorm migration:run
env:
DATABASE_DRIVER: mariadb
TYPEORM_CONNECTION: mariadb
TYPEORM_HOST: localhost
TYPEORM_PORT: 3306
TYPEORM_USERNAME: root
TYPEORM_PASSWORD: root
TYPEORM_DATABASE: ohmyform
TYPEORM_AUTO_SCHEMA_SYNC: false
TYPEORM_ENTITIES: src/entity/**/*.ts
TYPEORM_SUBSCRIBERS: src/subscriber/**/*.ts
TYPEORM_MIGRATIONS: src/migrations/mariadb/**/*.ts
TYPEORM_MIGRATIONS_TRANSACTION_MODE: 'each'
TYPEORM_ENTITIES_DIR: src/entity
TYPEORM_MIGRATIONS_DIR: src/migrations/mariadb
TYPEORM_SUBSCRIBERS_DIR: src/subscriber
run-sqlite:
name: Run SQLite
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Node.js dependencies
run: yarn install --frozen-lockfile --silent
- name: SQLite Migrations
run: yarn typeorm migration:run --transaction none
env:
DATABASE_DRIVER: sqlite
TYPEORM_CONNECTION: sqlite
TYPEORM_USERNAME: root
TYPEORM_DATABASE: data.sqlite
TYPEORM_AUTO_SCHEMA_SYNC: false
TYPEORM_ENTITIES: src/entity/**/*.ts
TYPEORM_SUBSCRIBERS: src/subscriber/**/*.ts
TYPEORM_MIGRATIONS: src/migrations/sqlite/**/*.ts
TYPEORM_MIGRATIONS_TRANSACTION_MODE: 'none'
TYPEORM_ENTITIES_DIR: src/entity
TYPEORM_MIGRATIONS_DIR: src/migrations/sqlite
TYPEORM_SUBSCRIBERS_DIR: src/subscriber

11
.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: node_js
node_js:
- 16
cache:
directories:
- node_modules
script:
- yarn
- yarn lint

View File

@ -5,79 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
<!--
Template for next version
## [Unreleased] ## [Unreleased]
### Added ### Added
### Changed
### Fixed
### Security
-->
## [Unreleased]
### Added
### Changed
### Fixed
- creation of new logic elements
- node prune location (https://github.com/ohmyform/ohmyform/issues/184)
### Security
## [1.0.3] - 2022-03-27
### Added
- add start and end page to form create call
### Changed
- notifications / hooks / pages and buttons encode and decode their ids
### Fixed
- missing encode / decode for form fields within submissions (https://github.com/ohmyform/ui/commit/30ff2c96bca20c1641d9cbb96c34cce934e1afea#r68602651)
- form field resolvers were missing
- node-gyp update to enable build on osx 12.3
- creating of new fields
### Security
- form hooks should only be queryable for form admins
## [1.0.2] - 2022-03-13
### Fixed
- error sending notification when field is not defined (https://github.com/ohmyform/ohmyform/issues/161)
## [1.0.1] - 2022-03-01
### Added
- allow one field nested data to be submitted
### Fixed
- only update user fields in update mutation if they changed
- form delete
- field submission without value field
### Security
- start using hashids to prevent insights into form ids (https://hashids.org/javascript/)
## [1.0.0] - 2022-02-28
### Added
- logic backend components - logic backend components
- forms now have multiple notification - forms now have multiple notification
- layout for forms - layout for forms
@ -88,7 +19,6 @@ Template for next version
- ability to load submission by id if token is present - ability to load submission by id if token is present
- anonymous form submissions (fixes https://github.com/ohmyform/ohmyform/issues/108) - anonymous form submissions (fixes https://github.com/ohmyform/ohmyform/issues/108)
- ability to filter for partial / completed or empty submissions - ability to filter for partial / completed or empty submissions
- migration tests for all commits
### Changed ### Changed

View File

@ -6,7 +6,7 @@ WORKDIR /usr/src/app
RUN apk update && apk add curl bash && rm -rf /var/cache/apk/* RUN apk update && apk add curl bash && rm -rf /var/cache/apk/*
# install node-prune (https://github.com/tj/node-prune) # install node-prune (https://github.com/tj/node-prune)
RUN curl -sf https://gobinaries.com/tj/node-prune | sh RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin
# just copy everhing # just copy everhing
COPY . . COPY . .

View File

@ -1,6 +1,6 @@
{ {
"name": "ohmyform-api", "name": "ohmyform-api",
"version": "1.0.3", "version": "1.0.0",
"description": "", "description": "",
"author": "", "author": "",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@ -22,8 +22,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json", "test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm:sqlite": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_sqlite.json", "typeorm:sqlite": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_sqlite.json",
"typeorm:postgres": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_postgres.json", "typeorm:postgres": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_postgres.json",
"typeorm:mariadb": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_mariadb.json", "typeorm:mariadb": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_mariadb.json"
"typeorm": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js"
}, },
"dependencies": { "dependencies": {
"@ardatan/aggregate-error": "^0.0.6", "@ardatan/aggregate-error": "^0.0.6",
@ -51,7 +50,6 @@
"graphql-subscriptions": "^2.0.0", "graphql-subscriptions": "^2.0.0",
"graphql-tools": "^8.2.0", "graphql-tools": "^8.2.0",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"hashids": "^2.2.10",
"html-to-text": "^8.1.0", "html-to-text": "^8.1.0",
"inquirer": "^8.2.0", "inquirer": "^8.2.0",
"ioredis": "^4.28.5", "ioredis": "^4.28.5",
@ -85,7 +83,7 @@
"@types/handlebars": "^4.1.0", "@types/handlebars": "^4.1.0",
"@types/html-to-text": "^8.0.1", "@types/html-to-text": "^8.0.1",
"@types/inquirer": "^8.2.0", "@types/inquirer": "^8.2.0",
"@types/jest": "^27.4.1", "@types/jest": "27.4.1",
"@types/mjml": "^4.7.0", "@types/mjml": "^4.7.0",
"@types/node": "^17.0.21", "@types/node": "^17.0.21",
"@types/passport-jwt": "^3.0.6", "@types/passport-jwt": "^3.0.6",
@ -100,7 +98,6 @@
"eslint-plugin-nestjs": "^1.2.3", "eslint-plugin-nestjs": "^1.2.3",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"node-gyp": "^9.0.0",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"supertest": "^6.2.2", "supertest": "^6.2.2",
"ts-jest": "27.1.3", "ts-jest": "27.1.3",

View File

@ -1,13 +1,11 @@
import { commands } from './command' import { commands } from './command'
import { guards } from './guard' import { guards } from './guard'
import { pipes } from './pipe'
import { resolvers } from './resolver' import { resolvers } from './resolver'
import { services } from './service' import { services } from './service'
export const providers = [ export const providers = [
...commands,
...guards,
...pipes,
...resolvers, ...resolvers,
...commands,
...services, ...services,
...guards,
] ]

View File

@ -1,8 +1,8 @@
import { Field, ID, ObjectType } from '@nestjs/graphql' import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType('Deleted') @ObjectType('Deleted')
export class DeletedModel { export class DeletedModel {
@Field(() => ID) @Field()
id: string id: string
constructor(id: string) { constructor(id: string) {

View File

@ -3,8 +3,6 @@ import { PageButtonEntity } from '../../entity/page.button.entity'
@ObjectType('Button') @ObjectType('Button')
export class ButtonModel { export class ButtonModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -26,9 +24,8 @@ export class ButtonModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly color?: string readonly color?: string
constructor(id: string, button: Partial<PageButtonEntity>) { constructor(button: Partial<PageButtonEntity>) {
this._id = button.id this.id = button.id.toString()
this.id = id
this.url = button.url this.url = button.url
this.action = button.action this.action = button.action
this.text = button.text this.text = button.text

View File

@ -1,5 +1,4 @@
import { Field, InputType } from '@nestjs/graphql' import { Field, InputType } from '@nestjs/graphql'
import { PageInput } from './page.input'
@InputType('FormCreateInput') @InputType('FormCreateInput')
export class FormCreateInput { export class FormCreateInput {
@ -20,10 +19,4 @@ export class FormCreateInput {
@Field({ nullable: true }) @Field({ nullable: true })
readonly layout: string readonly layout: string
@Field({ nullable: true })
readonly startPage: PageInput
@Field({ nullable: true })
readonly endPage: PageInput
} }

View File

@ -3,8 +3,6 @@ import { FormFieldLogicEntity } from '../../entity/form.field.logic.entity'
@ObjectType('FormFieldLogic') @ObjectType('FormFieldLogic')
export class FormFieldLogicModel { export class FormFieldLogicModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -32,9 +30,8 @@ export class FormFieldLogicModel {
@Field() @Field()
readonly enabled: boolean readonly enabled: boolean
constructor(id: string, document: FormFieldLogicEntity) { constructor(document: FormFieldLogicEntity) {
this._id = document.id this.id = document.id.toString()
this.id = id
this.enabled = document.enabled this.enabled = document.enabled
this.formula = document.formula this.formula = document.formula

View File

@ -1,10 +1,11 @@
import { Field, ID, ObjectType } from '@nestjs/graphql' import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FormFieldEntity } from '../../entity/form.field.entity' import { FormFieldEntity } from '../../entity/form.field.entity'
import { FormFieldLogicModel } from './form.field.logic.model'
import { FormFieldOptionModel } from './form.field.option.model'
import { FormFieldRatingModel } from './form.field.rating.model'
@ObjectType('FormField') @ObjectType('FormField')
export class FormFieldModel { export class FormFieldModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -29,9 +30,17 @@ export class FormFieldModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly defaultValue: string readonly defaultValue: string
constructor(id: string, document: FormFieldEntity) { @Field(() => [FormFieldOptionModel])
this._id = document.id readonly options: FormFieldOptionModel[]
this.id = id
@Field(() => [FormFieldLogicModel])
readonly logic: FormFieldLogicModel[]
@Field(() => FormFieldRatingModel, { nullable: true })
readonly rating: FormFieldRatingModel
constructor(document: FormFieldEntity) {
this.id = document.id.toString()
this.idx = document.idx this.idx = document.idx
this.title = document.title this.title = document.title
this.slug = document.slug this.slug = document.slug
@ -39,5 +48,8 @@ export class FormFieldModel {
this.description = document.description this.description = document.description
this.required = document.required this.required = document.required
this.defaultValue = document.defaultValue this.defaultValue = document.defaultValue
this.options = document.options?.map(option => new FormFieldOptionModel(option)) || []
this.logic = document.logic?.map(logic => new FormFieldLogicModel(logic)) || []
this.rating = document.rating ? new FormFieldRatingModel(document.rating) : null
} }
} }

View File

@ -3,8 +3,6 @@ import { FormFieldOptionEntity } from '../../entity/form.field.option.entity'
@ObjectType('FormFieldOption') @ObjectType('FormFieldOption')
export class FormFieldOptionModel { export class FormFieldOptionModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -17,9 +15,8 @@ export class FormFieldOptionModel {
@Field() @Field()
readonly value: string readonly value: string
constructor(id: string, option: FormFieldOptionEntity) { constructor(option: FormFieldOptionEntity) {
this._id = option.id this.id = option.id.toString()
this.id = id
this.key = option.key this.key = option.key
this.title = option.title this.title = option.title
this.value = option.value this.value = option.value

View File

@ -3,8 +3,6 @@ import { FormHookEntity } from '../../entity/form.hook.entity'
@ObjectType('FormHook') @ObjectType('FormHook')
export class FormHookModel { export class FormHookModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -17,9 +15,8 @@ export class FormHookModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly format?: string readonly format?: string
constructor(id, hook: FormHookEntity) { constructor(hook: FormHookEntity) {
this._id = hook.id this.id = hook.id.toString()
this.id = id
this.enabled = hook.enabled this.enabled = hook.enabled
this.url = hook.url this.url = hook.url
this.format = hook.format this.format = hook.format

View File

@ -3,8 +3,6 @@ import { FormEntity } from '../../entity/form.entity'
@ObjectType('Form') @ObjectType('Form')
export class FormModel { export class FormModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -26,9 +24,8 @@ export class FormModel {
@Field() @Field()
readonly anonymousSubmission: boolean readonly anonymousSubmission: boolean
constructor(id: string, form: FormEntity) { constructor(form: FormEntity) {
this._id = form.id this.id = form.id.toString()
this.id = id
this.title = form.title this.title = form.title
this.created = form.created this.created = form.created
this.lastModified = form.lastModified this.lastModified = form.lastModified

View File

@ -3,8 +3,6 @@ import { FormNotificationEntity } from '../../entity/form.notification.entity'
@ObjectType('FormNotification') @ObjectType('FormNotification')
export class FormNotificationModel { export class FormNotificationModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -29,9 +27,8 @@ export class FormNotificationModel {
@Field() @Field()
readonly enabled: boolean readonly enabled: boolean
constructor(id: string, partial: Partial<FormNotificationEntity>) { constructor(partial: Partial<FormNotificationEntity>) {
this._id = partial.id this.id = partial.id.toString()
this.id = id
this.subject = partial.subject this.subject = partial.subject
this.htmlTemplate = partial.htmlTemplate this.htmlTemplate = partial.htmlTemplate
this.enabled = partial.enabled this.enabled = partial.enabled

View File

@ -18,6 +18,6 @@ export class PageInput {
@Field({ nullable: true }) @Field({ nullable: true })
readonly buttonText?: string readonly buttonText?: string
@Field(() => [ButtonInput], { nullable: true }) @Field(() => [ButtonInput])
readonly buttons: ButtonInput[] readonly buttons: ButtonInput[]
} }

View File

@ -1,10 +1,9 @@
import { Field, ID, ObjectType } from '@nestjs/graphql' import { Field, ID, ObjectType } from '@nestjs/graphql'
import { PageEntity } from '../../entity/page.entity' import { PageEntity } from '../../entity/page.entity'
import { ButtonModel } from './button.model'
@ObjectType('Page') @ObjectType('Page')
export class PageModel { export class PageModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -20,18 +19,22 @@ export class PageModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly buttonText?: string readonly buttonText?: string
constructor(id: string, page?: Partial<PageEntity>) { @Field(() => [ButtonModel])
readonly buttons: ButtonModel[]
constructor(page: Partial<PageEntity>) {
if (!page) { if (!page) {
this.id = id this.id = Math.random().toString()
this.show = false this.show = false
this.buttons = []
return return
} }
this._id = page.id this.id = page.id.toString()
this.id = id
this.show = page.show this.show = page.show
this.title = page.title this.title = page.title
this.paragraph = page.paragraph this.paragraph = page.paragraph
this.buttonText = page.buttonText this.buttonText = page.buttonText
this.buttons = (page.buttons || []).map(button => new ButtonModel(button))
} }
} }

View File

@ -7,8 +7,8 @@ export class ProfileModel extends UserModel {
@Field(() => [String]) @Field(() => [String])
readonly roles: string[] readonly roles: string[]
constructor(id: string, user: UserEntity) { constructor(user: UserEntity) {
super(id, user) super(user)
this.roles = user.roles this.roles = user.roles
} }

View File

@ -3,8 +3,6 @@ import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
@ObjectType('SubmissionField') @ObjectType('SubmissionField')
export class SubmissionFieldModel { export class SubmissionFieldModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -14,9 +12,8 @@ export class SubmissionFieldModel {
@Field() @Field()
readonly type: string readonly type: string
constructor(id: string, field: SubmissionFieldEntity) { constructor(field: SubmissionFieldEntity) {
this._id = field.id this.id = field.id.toString()
this.id = id
this.value = JSON.stringify(field.content) this.value = JSON.stringify(field.content)
this.type = field.type this.type = field.type
} }

View File

@ -5,10 +5,8 @@ import { GeoLocationModel } from './geo.location.model'
@ObjectType('Submission') @ObjectType('Submission')
export class SubmissionModel { export class SubmissionModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: number
@Field() @Field()
readonly ipAddr: string readonly ipAddr: string
@ -31,9 +29,8 @@ export class SubmissionModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly lastModified?: Date readonly lastModified?: Date
constructor(id: string, submission: SubmissionEntity) { constructor(submission: SubmissionEntity) {
this._id = submission.id this.id = submission.id
this.id = id
this.ipAddr = submission.ipAddr this.ipAddr = submission.ipAddr
this.geoLocation = new GeoLocationModel(submission.geoLocation) this.geoLocation = new GeoLocationModel(submission.geoLocation)

View File

@ -3,8 +3,6 @@ import { SubmissionEntity } from '../../entity/submission.entity'
@ObjectType('SubmissionProgress') @ObjectType('SubmissionProgress')
export class SubmissionProgressModel { export class SubmissionProgressModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -20,9 +18,8 @@ export class SubmissionProgressModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly lastModified?: Date readonly lastModified?: Date
constructor(id: string, submission: Partial<SubmissionEntity>) { constructor(submission: Partial<SubmissionEntity>) {
this._id = submission.id this.id = submission.id.toString()
this.id = id
this.timeElapsed = submission.timeElapsed this.timeElapsed = submission.timeElapsed
this.percentageComplete = submission.percentageComplete this.percentageComplete = submission.percentageComplete

View File

@ -3,8 +3,6 @@ import { UserEntity } from '../../entity/user.entity'
@ObjectType('User') @ObjectType('User')
export class UserModel { export class UserModel {
readonly _id: number
@Field(() => ID) @Field(() => ID)
readonly id: string readonly id: string
@ -38,9 +36,8 @@ export class UserModel {
@Field({ nullable: true }) @Field({ nullable: true })
readonly lastModified: Date readonly lastModified: Date
constructor(id: string, user: UserEntity) { constructor(user: UserEntity) {
this._id = user.id this.id = user.id.toString()
this.id = id
this.username = user.username this.username = user.username
this.email = user.email this.email = user.email

View File

@ -72,10 +72,4 @@ export class FormEntity {
@UpdateDateColumn() @UpdateDateColumn()
public lastModified: Date public lastModified: Date
constructor(partial?: Partial<FormEntity>) {
if (partial) {
Object.assign(this, partial)
}
}
} }

View File

@ -2,12 +2,8 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, RelationId } from 't
import { FormFieldEntity } from './form.field.entity' import { FormFieldEntity } from './form.field.entity'
import { SubmissionEntity } from './submission.entity' import { SubmissionEntity } from './submission.entity'
type Simple = string | number | boolean export interface SubmissionFieldContent {
[key: string]: string | string[] | number | number[] | boolean | boolean[]
export type SubmissionFieldContent = Simple | Simple[] | {
[key: string]: Simple | Simple[] | {
[key: string]: Simple | Simple[]
}
} }
@Entity({ name: 'submission_field' }) @Entity({ name: 'submission_field' })

View File

@ -1,10 +1,9 @@
import { QueryRunner } from 'typeorm' import { MigrationInterface, QueryRunner } from 'typeorm'
import { SqliteMigration } from '../sqlite.migration'
export class initial1619723437787 extends SqliteMigration { export class initial1619723437787 implements MigrationInterface {
name = 'initial1619723437787' name = 'initial1619723437787'
public async realUp(queryRunner: QueryRunner): Promise<void> { public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('CREATE TABLE "page" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "show" boolean NOT NULL, "title" varchar, "paragraph" text, "buttonText" varchar)'); await queryRunner.query('CREATE TABLE "page" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "show" boolean NOT NULL, "title" varchar, "paragraph" text, "buttonText" varchar)');
await queryRunner.query('CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "firstName" varchar, "lastName" varchar, "email" varchar(255) NOT NULL, "username" varchar(255) NOT NULL, "passwordHash" varchar NOT NULL, "salt" varchar, "provider" varchar NOT NULL, "roles" text NOT NULL, "language" varchar NOT NULL, "resetPasswordToken" varchar, "resetPasswordExpires" datetime, "token" varchar, "apiKey" varchar, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"))'); await queryRunner.query('CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "firstName" varchar, "lastName" varchar, "email" varchar(255) NOT NULL, "username" varchar(255) NOT NULL, "passwordHash" varchar NOT NULL, "salt" varchar, "provider" varchar NOT NULL, "roles" text NOT NULL, "language" varchar NOT NULL, "resetPasswordToken" varchar, "resetPasswordExpires" datetime, "token" varchar, "apiKey" varchar, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"))');
await queryRunner.query('CREATE TABLE "form_field_logic" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "formula" varchar NOT NULL, "action" varchar(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "FK_6098b83f6759445d8cfdd03d545" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4a8019f2b753cfb3216dc3001a6" FOREIGN KEY ("jumpToId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)'); await queryRunner.query('CREATE TABLE "form_field_logic" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "formula" varchar NOT NULL, "action" varchar(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "FK_6098b83f6759445d8cfdd03d545" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4a8019f2b753cfb3216dc3001a6" FOREIGN KEY ("jumpToId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)');
@ -17,10 +16,13 @@ export class initial1619723437787 extends SqliteMigration {
await queryRunner.query('CREATE TABLE "form_visitor" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "referrer" varchar, "ipAddr" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "updated" datetime NOT NULL DEFAULT (datetime(\'now\')), "formId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)'); await queryRunner.query('CREATE TABLE "form_visitor" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "referrer" varchar, "ipAddr" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "updated" datetime NOT NULL DEFAULT (datetime(\'now\')), "formId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)');
await queryRunner.query('CREATE TABLE "submission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ipAddr" varchar NOT NULL, "tokenHash" varchar NOT NULL, "timeElapsed" numeric NOT NULL, "percentageComplete" numeric NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), "formId" integer, "visitorId" integer, "userId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_6090e1d5cbf3433ffd14e3b53e7" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_95b73c7faf2c199f005fda5e8c8" FOREIGN KEY ("visitorId") REFERENCES "form_visitor" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_7bd626272858ef6464aa2579094" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)'); await queryRunner.query('CREATE TABLE "submission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ipAddr" varchar NOT NULL, "tokenHash" varchar NOT NULL, "timeElapsed" numeric NOT NULL, "percentageComplete" numeric NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), "formId" integer, "visitorId" integer, "userId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_6090e1d5cbf3433ffd14e3b53e7" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_95b73c7faf2c199f005fda5e8c8" FOREIGN KEY ("visitorId") REFERENCES "form_visitor" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_7bd626272858ef6464aa2579094" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)');
await queryRunner.query('CREATE TABLE "form" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "language" varchar(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), "adminId" integer, "startPageId" integer, "endPageId" integer, "analyticsGacode" varchar, "designFont" varchar, "designColorsBackground" varchar, "designColorsQuestion" varchar, "designColorsAnswer" varchar, "designColorsButton" varchar, "designColorsButtonactive" varchar, "designColorsButtontext" varchar, CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70" FOREIGN KEY ("startPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_e5d158932e43cfbf9958931ee01" FOREIGN KEY ("endPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)'); await queryRunner.query('CREATE TABLE "form" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "language" varchar(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" datetime NOT NULL DEFAULT (datetime(\'now\')), "lastModified" datetime NOT NULL DEFAULT (datetime(\'now\')), "adminId" integer, "startPageId" integer, "endPageId" integer, "analyticsGacode" varchar, "designFont" varchar, "designColorsBackground" varchar, "designColorsQuestion" varchar, "designColorsAnswer" varchar, "designColorsButton" varchar, "designColorsButtonactive" varchar, "designColorsButtontext" varchar, CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70" FOREIGN KEY ("startPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_e5d158932e43cfbf9958931ee01" FOREIGN KEY ("endPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)');
} }
public async realDown(queryRunner: QueryRunner): Promise<void> { public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP TABLE "form"'); await queryRunner.query('DROP TABLE "form"');
await queryRunner.query('DROP TABLE "submission"'); await queryRunner.query('DROP TABLE "submission"');
await queryRunner.query('DROP TABLE "form_visitor"'); await queryRunner.query('DROP TABLE "form_visitor"');

View File

@ -1,14 +1,13 @@
import { QueryRunner } from 'typeorm' import { MigrationInterface, QueryRunner } from 'typeorm'
import { SqliteMigration } from '../sqlite.migration'
export class defaultValue1645952169100 extends SqliteMigration { export class defaultValue1645952169100 implements MigrationInterface {
name = 'defaultValue1645952169100' name = 'defaultValue1645952169100'
public async realUp(queryRunner: QueryRunner): Promise<void> { public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "form_field" RENAME COLUMN "value" TO "defaultValue"'); await queryRunner.query('ALTER TABLE "form_field" RENAME COLUMN "value" TO "defaultValue"');
} }
public async realDown(queryRunner: QueryRunner): Promise<void> { public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "form_field" RENAME COLUMN "defaultValue" TO "value"'); await queryRunner.query('ALTER TABLE "form_field" RENAME COLUMN "defaultValue" TO "value"');
} }
} }

View File

@ -1,19 +0,0 @@
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'
import { FormEntity } from '../../entity/form.entity'
import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
@Injectable()
export class FormByIdPipe implements PipeTransform<string, Promise<FormEntity>> {
constructor(
private readonly formService: FormService,
private readonly idService: IdService,
) {
}
async transform(value: string, metadata: ArgumentMetadata): Promise<FormEntity> {
const id = this.idService.decode(value)
return await this.formService.findById(id)
}
}

View File

@ -1,3 +0,0 @@
import { FormByIdPipe } from './form.by.id.pipe'
export const formPipes = [FormByIdPipe]

View File

@ -1,9 +0,0 @@
import { formPipes } from './form'
import { submissionPipes } from './submission'
import { userPipes } from './user'
export const pipes = [
...formPipes,
...submissionPipes,
...userPipes,
]

View File

@ -1,3 +0,0 @@
import { SubmissionByIdPipe } from './submission.by.id.pipe'
export const submissionPipes = [SubmissionByIdPipe]

View File

@ -1,19 +0,0 @@
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'
import { SubmissionEntity } from '../../entity/submission.entity'
import { IdService } from '../../service/id.service'
import { SubmissionService } from '../../service/submission/submission.service'
@Injectable()
export class SubmissionByIdPipe implements PipeTransform<string, Promise<SubmissionEntity>> {
constructor(
private readonly submissionService: SubmissionService,
private readonly idService: IdService,
) {
}
async transform(value: string, metadata: ArgumentMetadata): Promise<SubmissionEntity> {
const id = this.idService.decode(value)
return await this.submissionService.findById(id)
}
}

View File

@ -1,3 +0,0 @@
import { UserByIdPipe } from './user.by.id.pipe'
export const userPipes = [UserByIdPipe]

View File

@ -1,19 +0,0 @@
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'
import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { UserService } from '../../service/user/user.service'
@Injectable()
export class UserByIdPipe implements PipeTransform<string, Promise<UserEntity>> {
constructor(
private readonly userService: UserService,
private readonly idService: IdService,
) {
}
async transform(value: string, metadata: ArgumentMetadata): Promise<UserEntity> {
const id = this.idService.decode(value)
return await this.userService.findById(id)
}
}

View File

@ -1,10 +1,12 @@
type ID = string | number
export class ContextCache<A = any> { export class ContextCache<A = any> {
private cache: { private cache: {
[key: string]: any [key: string]: any
} = {} } = {}
public getCacheKey(type: string, id: number): string { public getCacheKey(type: string, id: ID): string {
return `${type}:${id}` return `${type}:${id}`
} }

View File

@ -7,14 +7,12 @@ import { FormModel } from '../../dto/form/form.model'
import { FormEntity } from '../../entity/form.entity' import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormCreateService } from '../../service/form/form.create.service' import { FormCreateService } from '../../service/form/form.create.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class FormCreateMutation { export class FormCreateMutation {
constructor( constructor(
private readonly createService: FormCreateService, private readonly createService: FormCreateService
private readonly idService: IdService,
) { ) {
} }
@ -29,6 +27,6 @@ export class FormCreateMutation {
cache.add(cache.getCacheKey(FormEntity.name, form.id), form) cache.add(cache.getCacheKey(FormEntity.name, form.id), form)
return new FormModel(this.idService.encode(form.id), form) return new FormModel(form)
} }
} }

View File

@ -3,19 +3,15 @@ import { Args, ID, Mutation } from '@nestjs/graphql'
import { Roles } from '../../decorator/roles.decorator' import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator' import { User } from '../../decorator/user.decorator'
import { DeletedModel } from '../../dto/deleted.model' import { DeletedModel } from '../../dto/deleted.model'
import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormByIdPipe } from '../../pipe/form/form.by.id.pipe'
import { FormDeleteService } from '../../service/form/form.delete.service' import { FormDeleteService } from '../../service/form/form.delete.service'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
@Injectable() @Injectable()
export class FormDeleteMutation { export class FormDeleteMutation {
constructor( constructor(
private readonly deleteService: FormDeleteService, private readonly deleteService: FormDeleteService,
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@ -23,14 +19,16 @@ export class FormDeleteMutation {
@Roles('admin') @Roles('admin')
async deleteForm( async deleteForm(
@User() user: UserEntity, @User() user: UserEntity,
@Args('id', {type: () => ID}, FormByIdPipe) form: FormEntity, @Args({ name: 'id', type: () => ID}) id: string,
): Promise<DeletedModel> { ): Promise<DeletedModel> {
const form = await this.formService.findById(id)
if (!form.isLive && !this.formService.isAdmin(form, user)) { if (!form.isLive && !this.formService.isAdmin(form, user)) {
throw new Error('invalid form') throw new Error('invalid form')
} }
await this.deleteService.delete(form.id) await this.deleteService.delete(id)
return new DeletedModel(this.idService.encode(form.id)) return new DeletedModel(id)
} }
} }

View File

@ -1,73 +0,0 @@
import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql'
import { FormFieldLogicModel } from '../../dto/form/form.field.logic.model'
import { FormFieldModel } from '../../dto/form/form.field.model'
import { FormFieldOptionModel } from '../../dto/form/form.field.option.model'
import { FormFieldRatingModel } from '../../dto/form/form.field.rating.model'
import { FormFieldEntity } from '../../entity/form.field.entity'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache'
@Resolver(FormFieldModel)
export class FormFieldResolver {
constructor(
private readonly idService: IdService,
) {
}
@ResolveField(() => [FormFieldOptionModel])
async options(
@Parent() parent: FormFieldModel,
@Context('cache') cache: ContextCache,
): Promise<FormFieldOptionModel[]> {
const field = await cache.get<FormFieldEntity>(cache.getCacheKey(
FormFieldEntity.name,
parent._id
))
if (!field.options) {
return []
}
return field.options.map(option => new FormFieldOptionModel(
this.idService.encode(option.id),
option,
))
}
@ResolveField(() => [FormFieldLogicModel])
async logic(
@Parent() parent: FormFieldModel,
@Context('cache') cache: ContextCache,
): Promise<FormFieldLogicModel[]> {
const field = await cache.get<FormFieldEntity>(cache.getCacheKey(
FormFieldEntity.name,
parent._id
))
if (!field.logic) {
return []
}
return field.logic.map(logic => new FormFieldLogicModel(
this.idService.encode(logic.id),
logic,
))
}
@ResolveField(() => FormFieldRatingModel, { nullable: true })
async rating(
@Parent() parent: FormFieldModel,
@Context('cache') cache: ContextCache,
): Promise<FormFieldRatingModel> {
const field = await cache.get<FormFieldEntity>(cache.getCacheKey(
FormFieldEntity.name,
parent._id
))
if (!field.rating) {
return null
}
return new FormFieldRatingModel(field.rating)
}
}

View File

@ -7,14 +7,12 @@ import { FormPagerModel } from '../../dto/form/form.pager.model'
import { FormEntity } from '../../entity/form.entity' import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class FormListQuery { export class FormListQuery {
constructor( constructor(
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@ -36,7 +34,7 @@ export class FormListQuery {
forms.forEach(form => cache.add(cache.getCacheKey(FormEntity.name, form.id), form)) forms.forEach(form => cache.add(cache.getCacheKey(FormEntity.name, form.id), form))
return new FormPagerModel( return new FormPagerModel(
forms.map(form => new FormModel(this.idService.encode(form.id), form)), forms.map(form => new FormModel(form)),
total, total,
limit, limit,
start, start,

View File

@ -4,31 +4,30 @@ import { User } from '../../decorator/user.decorator'
import { FormModel } from '../../dto/form/form.model' import { FormModel } from '../../dto/form/form.model'
import { FormEntity } from '../../entity/form.entity' import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormByIdPipe } from '../../pipe/form/form.by.id.pipe'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class FormQuery { export class FormQuery {
constructor( constructor(
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@Query(() => FormModel) @Query(() => FormModel)
getFormById( async getFormById(
@User() user: UserEntity, @User() user: UserEntity,
@Args('id', {type: () => ID}, FormByIdPipe) form: FormEntity, @Args('id', {type: () => ID}) id,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): FormModel { ): Promise<FormModel> {
const form = await this.formService.findById(id)
if (!form.isLive && !this.formService.isAdmin(form, user)) { if (!form.isLive && !this.formService.isAdmin(form, user)) {
throw new Error('invalid form') throw new Error('invalid form')
} }
cache.add(cache.getCacheKey(FormEntity.name, form.id), form) cache.add(cache.getCacheKey(FormEntity.name, form.id), form)
return new FormModel(this.idService.encode(form.id), form) return new FormModel(form)
} }
} }

View File

@ -9,18 +9,14 @@ import { FormNotificationModel } from '../../dto/form/form.notification.model'
import { PageModel } from '../../dto/form/page.model' import { PageModel } from '../../dto/form/page.model'
import { UserModel } from '../../dto/user/user.model' import { UserModel } from '../../dto/user/user.model'
import { FormEntity } from '../../entity/form.entity' import { FormEntity } from '../../entity/form.entity'
import { FormFieldEntity } from '../../entity/form.field.entity'
import { PageEntity } from '../../entity/page.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Resolver(() => FormModel) @Resolver(() => FormModel)
export class FormResolver { export class FormResolver {
constructor( constructor(
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@ -30,45 +26,20 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<FormFieldModel[]> { ): Promise<FormFieldModel[]> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (!form.fields) { return form.fields?.map(field => new FormFieldModel(field)).sort((a,b) => a.idx - b.idx) || []
return []
}
return form.fields
.sort((a,b) => a.idx - b.idx)
.map(field => {
cache.add(cache.getCacheKey(FormFieldEntity.name, field.id), field)
return new FormFieldModel(
this.idService.encode(field.id),
field,
)
})
} }
@ResolveField(() => [FormHookModel]) @ResolveField(() => [FormHookModel])
@Roles('admin')
async hooks( async hooks(
@User() user: UserEntity, @User() user: UserEntity,
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<FormHookModel[]> { ): Promise<FormHookModel[]> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (!this.formService.isAdmin(form, user)) { return form.hooks?.map(hook => new FormHookModel(hook)) || []
throw new Error('no access to field')
}
if (!form.hooks) {
return []
}
return form.hooks.map(hook => new FormHookModel(
this.idService.encode(hook.id),
hook
))
} }
@ResolveField(() => Boolean) @ResolveField(() => Boolean)
@ -78,7 +49,7 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<boolean> { ): Promise<boolean> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (!this.formService.isAdmin(form, user)) { if (!this.formService.isAdmin(form, user)) {
throw new Error('no access to field') throw new Error('no access to field')
@ -94,20 +65,13 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<FormNotificationModel[]> { ): Promise<FormNotificationModel[]> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (!this.formService.isAdmin(form, user)) { if (!this.formService.isAdmin(form, user)) {
throw new Error('no access to field') throw new Error('no access to field')
} }
if (!form.notifications) { return form.notifications?.map(notification => new FormNotificationModel(notification)) || []
return []
}
return form.notifications.map(notification => new FormNotificationModel(
this.idService.encode(notification.id),
notification
))
} }
@ResolveField(() => DesignModel) @ResolveField(() => DesignModel)
@ -116,7 +80,7 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<DesignModel> { ): Promise<DesignModel> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
return new DesignModel(form.design) return new DesignModel(form.design)
} }
@ -126,21 +90,9 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<PageModel> { ): Promise<PageModel> {
const { startPage } = await cache.get<FormEntity>(cache.getCacheKey( const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
FormEntity.name,
parent._id
))
if (startPage) { return new PageModel(form.startPage)
cache.add(cache.getCacheKey(PageEntity.name, startPage.id), startPage)
return new PageModel(
this.idService.encode(startPage.id),
startPage
)
}
return new PageModel(Math.random().toString())
} }
@ResolveField(() => PageModel) @ResolveField(() => PageModel)
@ -148,18 +100,9 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<PageModel> { ): Promise<PageModel> {
const { endPage } = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (endPage) { return new PageModel(form.endPage)
cache.add(cache.getCacheKey(PageEntity.name, endPage.id), endPage)
return new PageModel(
this.idService.encode(endPage.id),
endPage
)
}
return new PageModel(Math.random().toString())
} }
@ResolveField(() => UserModel, { nullable: true }) @ResolveField(() => UserModel, { nullable: true })
@ -168,12 +111,12 @@ export class FormResolver {
@Parent() parent: FormModel, @Parent() parent: FormModel,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<UserModel> { ): Promise<UserModel> {
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent._id)) const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
if (!form.admin) { if (!form.admin) {
return null return null
} }
return new UserModel(this.idService.encode(form.admin.id), form.admin) return new UserModel(form.admin)
} }
} }

View File

@ -8,7 +8,6 @@ import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { FormUpdateService } from '../../service/form/form.update.service' import { FormUpdateService } from '../../service/form/form.update.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
@ -16,7 +15,6 @@ export class FormUpdateMutation {
constructor( constructor(
private readonly updateService: FormUpdateService, private readonly updateService: FormUpdateService,
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@ -27,7 +25,7 @@ export class FormUpdateMutation {
@Args({ name: 'form', type: () => FormUpdateInput }) input: FormUpdateInput, @Args({ name: 'form', type: () => FormUpdateInput }) input: FormUpdateInput,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<FormModel> { ): Promise<FormModel> {
const form = await this.formService.findById(this.idService.decode(input.id)) const form = await this.formService.findById(input.id)
if (!form.isLive && !this.formService.isAdmin(form, user)) { if (!form.isLive && !this.formService.isAdmin(form, user)) {
throw new Error('invalid form') throw new Error('invalid form')
@ -37,6 +35,6 @@ export class FormUpdateMutation {
cache.add(cache.getCacheKey(FormEntity.name, form.id), form) cache.add(cache.getCacheKey(FormEntity.name, form.id), form)
return new FormModel(this.idService.encode(form.id), form) return new FormModel(form)
} }
} }

View File

@ -1,23 +1,19 @@
import { FormCreateMutation } from './form.create.mutation' import { FormCreateMutation } from './form.create.mutation'
import { FormDeleteMutation } from './form.delete.mutation' import { FormDeleteMutation } from './form.delete.mutation'
import { FormFieldResolver } from './form.field.resolver'
import { FormListQuery } from './form.list.query' import { FormListQuery } from './form.list.query'
import { FormQuery } from './form.query' import { FormQuery } from './form.query'
import { FormResolver } from './form.resolver' import { FormResolver } from './form.resolver'
import { FormStatisticQuery } from './form.statistic.query' import { FormStatisticQuery } from './form.statistic.query'
import { FormStatisticResolver } from './form.statistic.resolver' import { FormStatisticResolver } from './form.statistic.resolver'
import { FormUpdateMutation } from './form.update.mutation' import { FormUpdateMutation } from './form.update.mutation'
import { PageResolver } from './page.resolver'
export const formResolvers = [ export const formResolvers = [
FormCreateMutation, FormCreateMutation,
FormDeleteMutation, FormDeleteMutation,
FormFieldResolver,
FormQuery, FormQuery,
FormResolver, FormResolver,
FormListQuery, FormListQuery,
FormStatisticQuery, FormStatisticQuery,
FormStatisticResolver, FormStatisticResolver,
FormUpdateMutation, FormUpdateMutation,
PageResolver,
] ]

View File

@ -1,32 +0,0 @@
import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql'
import { ButtonModel } from '../../dto/form/button.model'
import { FormModel } from '../../dto/form/form.model'
import { PageModel } from '../../dto/form/page.model'
import { PageEntity } from '../../entity/page.entity'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache'
@Resolver(() => PageModel)
export class PageResolver {
constructor(
private readonly idService: IdService,
) {
}
@ResolveField(() => [ButtonModel])
async buttons(
@Parent() parent: FormModel,
@Context('cache') cache: ContextCache,
): Promise<ButtonModel[]> {
if (!parent._id) {
return []
}
const page = await cache.get<PageEntity>(cache.getCacheKey(PageEntity.name, parent._id))
return page.buttons.map(button => new ButtonModel(
this.idService.encode(button.id),
button
))
}
}

View File

@ -4,16 +4,10 @@ import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator' import { User } from '../../decorator/user.decorator'
import { ProfileModel } from '../../dto/profile/profile.model' import { ProfileModel } from '../../dto/profile/profile.model'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class ProfileQuery { export class ProfileQuery {
constructor(
private readonly idService: IdService,
) {
}
@Query(() => ProfileModel) @Query(() => ProfileModel)
@Roles('user') @Roles('user')
public me( public me(
@ -22,6 +16,6 @@ export class ProfileQuery {
): ProfileModel { ): ProfileModel {
cache.add(cache.getCacheKey(UserEntity.name, user.id), user) cache.add(cache.getCacheKey(UserEntity.name, user.id), user)
return new ProfileModel(this.idService.encode(user.id), user) return new ProfileModel(user)
} }
} }

View File

@ -5,7 +5,6 @@ import { User } from '../../decorator/user.decorator'
import { ProfileModel } from '../../dto/profile/profile.model' import { ProfileModel } from '../../dto/profile/profile.model'
import { ProfileUpdateInput } from '../../dto/profile/profile.update.input' import { ProfileUpdateInput } from '../../dto/profile/profile.update.input'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { ProfileUpdateService } from '../../service/profile/profile.update.service' import { ProfileUpdateService } from '../../service/profile/profile.update.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -13,7 +12,6 @@ import { ContextCache } from '../context.cache'
export class ProfileUpdateMutation { export class ProfileUpdateMutation {
constructor( constructor(
private readonly updateService: ProfileUpdateService, private readonly updateService: ProfileUpdateService,
private readonly idService: IdService,
) { ) {
} }
@ -28,7 +26,7 @@ export class ProfileUpdateMutation {
cache.add(cache.getCacheKey(UserEntity.name, user.id), user) cache.add(cache.getCacheKey(UserEntity.name, user.id), user)
return new ProfileModel(this.idService.encode(user.id), user) return new ProfileModel(user)
} }
@Mutation(() => ProfileModel) @Mutation(() => ProfileModel)
@ -42,6 +40,6 @@ export class ProfileUpdateMutation {
cache.add(cache.getCacheKey(UserEntity.name, user.id), user) cache.add(cache.getCacheKey(UserEntity.name, user.id), user)
return new ProfileModel(this.idService.encode(user.id), user) return new ProfileModel(user)
} }
} }

View File

@ -4,14 +4,12 @@ import { SubmissionFieldModel } from '../../dto/submission/submission.field.mode
import { FormFieldEntity } from '../../entity/form.field.entity' import { FormFieldEntity } from '../../entity/form.field.entity'
import { SubmissionFieldEntity } from '../../entity/submission.field.entity' import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
import { FormFieldService } from '../../service/form/form.field.service' import { FormFieldService } from '../../service/form/form.field.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Resolver(() => SubmissionFieldModel) @Resolver(() => SubmissionFieldModel)
export class SubmissionFieldResolver { export class SubmissionFieldResolver {
constructor( constructor(
private readonly formFieldService: FormFieldService, private readonly formFieldService: FormFieldService,
private readonly idService: IdService,
) { ) {
} }
@ -21,7 +19,7 @@ export class SubmissionFieldResolver {
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<FormFieldModel> { ): Promise<FormFieldModel> {
const submissionField = await cache.get<SubmissionFieldEntity>( const submissionField = await cache.get<SubmissionFieldEntity>(
cache.getCacheKey(SubmissionFieldEntity.name, parent._id) cache.getCacheKey(SubmissionFieldEntity.name, parent.id)
) )
const field = await cache.get<FormFieldEntity>( const field = await cache.get<FormFieldEntity>(
@ -35,9 +33,6 @@ export class SubmissionFieldResolver {
return null return null
} }
return new FormFieldModel( return new FormFieldModel(field)
this.idService.encode(field.id),
field,
)
} }
} }

View File

@ -2,31 +2,33 @@ import { Injectable } from '@nestjs/common'
import { Args, Context, ID, Mutation } from '@nestjs/graphql' import { Args, Context, ID, Mutation } from '@nestjs/graphql'
import { User } from '../../decorator/user.decorator' import { User } from '../../decorator/user.decorator'
import { SubmissionProgressModel } from '../../dto/submission/submission.progress.model' import { SubmissionProgressModel } from '../../dto/submission/submission.progress.model'
import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.field.input'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { SubmissionByIdPipe } from '../../pipe/submission/submission.by.id.pipe' import { SubmissionService } from '../../service/submission/submission.service'
import { IdService } from '../../service/id.service'
import { SubmissionSetFieldService } from '../../service/submission/submission.set.field.service' import { SubmissionSetFieldService } from '../../service/submission/submission.set.field.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class SubmissionFinishMutation { export class SubmissionFinishMutation {
constructor( constructor(
private readonly submissionService: SubmissionService,
private readonly setFieldService: SubmissionSetFieldService, private readonly setFieldService: SubmissionSetFieldService,
private readonly idService: IdService,
) { ) {
} }
@Mutation(() => SubmissionProgressModel) @Mutation(() => SubmissionProgressModel)
async submissionFinish( async submissionFinish(
@User() user: UserEntity, @User() user: UserEntity,
@Args({ name: 'submission', type: () => ID }, SubmissionByIdPipe) submission: SubmissionEntity, @Args({ name: 'submission', type: () => ID }) id: string,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionProgressModel> { ): Promise<SubmissionProgressModel> {
const submission = await this.submissionService.findById(id)
await this.setFieldService.finishSubmission(submission) await this.setFieldService.finishSubmission(submission)
cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission) cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission)
return new SubmissionProgressModel(this.idService.encode(submission.id), submission) return new SubmissionProgressModel(submission)
} }
} }

View File

@ -4,31 +4,31 @@ import { User } from '../../decorator/user.decorator'
import { SubmissionModel } from '../../dto/submission/submission.model' import { SubmissionModel } from '../../dto/submission/submission.model'
import { SubmissionPagerFilterInput } from '../../dto/submission/submission.pager.filter.input' import { SubmissionPagerFilterInput } from '../../dto/submission/submission.pager.filter.input'
import { SubmissionPagerModel } from '../../dto/submission/submission.pager.model' import { SubmissionPagerModel } from '../../dto/submission/submission.pager.model'
import { FormEntity } from '../../entity/form.entity'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormByIdPipe } from '../../pipe/form/form.by.id.pipe' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
import { SubmissionService } from '../../service/submission/submission.service' import { SubmissionService } from '../../service/submission/submission.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class SubmissionListQuery { export class SubmissionListQuery {
constructor( constructor(
private readonly formService: FormService,
private readonly submissionService: SubmissionService, private readonly submissionService: SubmissionService,
private readonly idService: IdService,
) { ) {
} }
@Query(() => SubmissionPagerModel) @Query(() => SubmissionPagerModel)
async listSubmissions( async listSubmissions(
@User() user: UserEntity, @User() user: UserEntity,
@Args('form', {type: () => ID}, FormByIdPipe) form: FormEntity, @Args('form', {type: () => ID}) id: string,
@Args('start', {type: () => Int, defaultValue: 0, nullable: true}) start: number, @Args('start', {type: () => Int, defaultValue: 0, nullable: true}) start: number,
@Args('limit', {type: () => Int, defaultValue: 50, nullable: true}) limit: number, @Args('limit', {type: () => Int, defaultValue: 50, nullable: true}) limit: number,
@Args('filter', {type: () => SubmissionPagerFilterInput, defaultValue: new SubmissionPagerFilterInput()}) filter: SubmissionPagerFilterInput, @Args('filter', {type: () => SubmissionPagerFilterInput, defaultValue: new SubmissionPagerFilterInput()}) filter: SubmissionPagerFilterInput,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionPagerModel> { ): Promise<SubmissionPagerModel> {
const form = await this.formService.findById(id)
const [submissions, total] = await this.submissionService.find( const [submissions, total] = await this.submissionService.find(
form, form,
start, start,
@ -42,10 +42,7 @@ export class SubmissionListQuery {
}) })
return new SubmissionPagerModel( return new SubmissionPagerModel(
submissions.map(submission => new SubmissionModel( submissions.map(submission => new SubmissionModel(submission)),
this.idService.encode(submission.id),
submission
)),
total, total,
limit, limit,
start, start,

View File

@ -4,9 +4,8 @@ import { User } from '../../decorator/user.decorator'
import { SubmissionModel } from '../../dto/submission/submission.model' import { SubmissionModel } from '../../dto/submission/submission.model'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { SubmissionByIdPipe } from '../../pipe/submission/submission.by.id.pipe'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service' import { SubmissionService } from '../../service/submission/submission.service'
import { SubmissionTokenService } from '../../service/submission/submission.token.service' import { SubmissionTokenService } from '../../service/submission/submission.token.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -14,18 +13,20 @@ import { ContextCache } from '../context.cache'
export class SubmissionQuery { export class SubmissionQuery {
constructor( constructor(
private readonly formService: FormService, private readonly formService: FormService,
private readonly submissionService: SubmissionService,
private readonly tokenService: SubmissionTokenService, private readonly tokenService: SubmissionTokenService,
private readonly idService: IdService,
) { ) {
} }
@Query(() => SubmissionModel) @Query(() => SubmissionModel)
async getSubmissionById( async getSubmissionById(
@User() user: UserEntity, @User() user: UserEntity,
@Args('id', {type: () => ID}, SubmissionByIdPipe) submission: SubmissionEntity, @Args('id', {type: () => ID}) id: string,
@Args('token', {nullable: true}) token: string, @Args('token', {nullable: true}) token: string,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionModel> { ): Promise<SubmissionModel> {
const submission = await this.submissionService.findById(id)
if ( if (
!await this.tokenService.verify(token, submission.tokenHash) !await this.tokenService.verify(token, submission.tokenHash)
&& !this.formService.isAdmin(submission.form, user) && !this.formService.isAdmin(submission.form, user)
@ -35,6 +36,6 @@ export class SubmissionQuery {
cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission) cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission)
return new SubmissionModel(this.idService.encode(submission.id), submission) return new SubmissionModel(submission)
} }
} }

View File

@ -5,16 +5,10 @@ import { SubmissionModel } from '../../dto/submission/submission.model'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { SubmissionFieldEntity } from '../../entity/submission.field.entity' import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Resolver(() => SubmissionModel) @Resolver(() => SubmissionModel)
export class SubmissionResolver { export class SubmissionResolver {
constructor(
private readonly idService: IdService,
) {
}
@ResolveField(() => [SubmissionFieldModel]) @ResolveField(() => [SubmissionFieldModel])
async fields( async fields(
@User() user: UserEntity, @User() user: UserEntity,
@ -22,12 +16,12 @@ export class SubmissionResolver {
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionFieldModel[]> { ): Promise<SubmissionFieldModel[]> {
const submission = await cache.get<SubmissionEntity>( const submission = await cache.get<SubmissionEntity>(
cache.getCacheKey(SubmissionEntity.name, parent._id) cache.getCacheKey(SubmissionEntity.name, parent.id)
) )
return submission.fields.map(field => { return submission.fields.map(field => {
cache.add(cache.getCacheKey(SubmissionFieldEntity.name, field.id), field) cache.add(cache.getCacheKey(SubmissionFieldEntity.name, field.id), field)
return new SubmissionFieldModel(this.idService.encode(field.id), field) return new SubmissionFieldModel(field)
}) })
} }
} }

View File

@ -5,8 +5,6 @@ import { SubmissionProgressModel } from '../../dto/submission/submission.progres
import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.field.input' import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.field.input'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { SubmissionByIdPipe } from '../../pipe/submission/submission.by.id.pipe'
import { IdService } from '../../service/id.service'
import { SubmissionService } from '../../service/submission/submission.service' import { SubmissionService } from '../../service/submission/submission.service'
import { SubmissionSetFieldService } from '../../service/submission/submission.set.field.service' import { SubmissionSetFieldService } from '../../service/submission/submission.set.field.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -16,17 +14,18 @@ export class SubmissionSetFieldMutation {
constructor( constructor(
private readonly submissionService: SubmissionService, private readonly submissionService: SubmissionService,
private readonly setFieldService: SubmissionSetFieldService, private readonly setFieldService: SubmissionSetFieldService,
private readonly idService: IdService,
) { ) {
} }
@Mutation(() => SubmissionProgressModel) @Mutation(() => SubmissionProgressModel)
async submissionSetField( async submissionSetField(
@User() user: UserEntity, @User() user: UserEntity,
@Args({ name: 'submission', type: () => ID }, SubmissionByIdPipe) submission: SubmissionEntity, @Args({ name: 'submission', type: () => ID }) id: string,
@Args({ name: 'field', type: () => SubmissionSetFieldInput }) input: SubmissionSetFieldInput, @Args({ name: 'field', type: () => SubmissionSetFieldInput }) input: SubmissionSetFieldInput,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionProgressModel> { ): Promise<SubmissionProgressModel> {
const submission = await this.submissionService.findById(id)
if (!await this.submissionService.isOwner(submission, input.token)) { if (!await this.submissionService.isOwner(submission, input.token)) {
throw new Error('no access to submission') throw new Error('no access to submission')
} }
@ -35,6 +34,6 @@ export class SubmissionSetFieldMutation {
cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission) cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission)
return new SubmissionProgressModel(this.idService.encode(submission.id), submission) return new SubmissionProgressModel(submission)
} }
} }

View File

@ -4,12 +4,9 @@ import { IpAddress } from '../../decorator/ip.address.decorator'
import { User } from '../../decorator/user.decorator' import { User } from '../../decorator/user.decorator'
import { SubmissionProgressModel } from '../../dto/submission/submission.progress.model' import { SubmissionProgressModel } from '../../dto/submission/submission.progress.model'
import { SubmissionStartInput } from '../../dto/submission/submission.start.input' import { SubmissionStartInput } from '../../dto/submission/submission.start.input'
import { FormEntity } from '../../entity/form.entity'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormByIdPipe } from '../../pipe/form/form.by.id.pipe'
import { FormService } from '../../service/form/form.service' import { FormService } from '../../service/form/form.service'
import { IdService } from '../../service/id.service'
import { SubmissionStartService } from '../../service/submission/submission.start.service' import { SubmissionStartService } from '../../service/submission/submission.start.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -18,18 +15,19 @@ export class SubmissionStartMutation {
constructor( constructor(
private readonly startService: SubmissionStartService, private readonly startService: SubmissionStartService,
private readonly formService: FormService, private readonly formService: FormService,
private readonly idService: IdService,
) { ) {
} }
@Mutation(() => SubmissionProgressModel) @Mutation(() => SubmissionProgressModel)
async submissionStart( async submissionStart(
@User() user: UserEntity, @User() user: UserEntity,
@Args({ name: 'form', type: () => ID }, FormByIdPipe) form: FormEntity, @Args({ name: 'form', type: () => ID }) id: string,
@Args({ name: 'submission', type: () => SubmissionStartInput }) input: SubmissionStartInput, @Args({ name: 'submission', type: () => SubmissionStartInput }) input: SubmissionStartInput,
@IpAddress() ipAddr: string, @IpAddress() ipAddr: string,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<SubmissionProgressModel> { ): Promise<SubmissionProgressModel> {
const form = await this.formService.findById(id)
if (!form.isLive && !this.formService.isAdmin(form, user)) { if (!form.isLive && !this.formService.isAdmin(form, user)) {
throw new Error('invalid form') throw new Error('invalid form')
} }
@ -38,6 +36,6 @@ export class SubmissionStartMutation {
cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission) cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission)
return new SubmissionProgressModel(this.idService.encode(submission.id), submission) return new SubmissionProgressModel(submission)
} }
} }

View File

@ -4,15 +4,12 @@ import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator' import { User } from '../../decorator/user.decorator'
import { DeletedModel } from '../../dto/deleted.model' import { DeletedModel } from '../../dto/deleted.model'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { UserByIdPipe } from '../../pipe/user/user.by.id.pipe'
import { IdService } from '../../service/id.service'
import { UserDeleteService } from '../../service/user/user.delete.service' import { UserDeleteService } from '../../service/user/user.delete.service'
@Injectable() @Injectable()
export class UserDeleteMutation { export class UserDeleteMutation {
constructor( constructor(
private readonly deleteService: UserDeleteService, private readonly deleteService: UserDeleteService,
private readonly idService: IdService,
) { ) {
} }
@ -20,14 +17,14 @@ export class UserDeleteMutation {
@Roles('admin') @Roles('admin')
async deleteUser( async deleteUser(
@User() auth: UserEntity, @User() auth: UserEntity,
@Args({ name: 'id', type: () => ID}, UserByIdPipe) user: UserEntity, @Args({ name: 'id', type: () => ID}) id: string,
): Promise<DeletedModel> { ): Promise<DeletedModel> {
if (auth.id === user.id) { if (auth.id.toString() === id) {
throw new Error('cannot delete your own user') throw new Error('cannot delete your own user')
} }
await this.deleteService.delete(user.id) await this.deleteService.delete(id)
return new DeletedModel(this.idService.encode(user.id)) return new DeletedModel(id)
} }
} }

View File

@ -3,7 +3,6 @@ import { Roles } from '../../decorator/roles.decorator'
import { UserModel } from '../../dto/user/user.model' import { UserModel } from '../../dto/user/user.model'
import { UserPagerModel } from '../../dto/user/user.pager.model' import { UserPagerModel } from '../../dto/user/user.pager.model'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { UserService } from '../../service/user/user.service' import { UserService } from '../../service/user/user.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -11,7 +10,6 @@ import { ContextCache } from '../context.cache'
export class UserListQuery { export class UserListQuery {
constructor( constructor(
private readonly userService: UserService, private readonly userService: UserService,
private readonly idService: IdService,
) { ) {
} }
@ -27,7 +25,7 @@ export class UserListQuery {
return new UserPagerModel( return new UserPagerModel(
entities.map(entity => { entities.map(entity => {
cache.add(cache.getCacheKey(UserEntity.name, entity.id), entity) cache.add(cache.getCacheKey(UserEntity.name, entity.id), entity)
return new UserModel(this.idService.encode(entity.id), entity) return new UserModel(entity)
}), }),
total, total,
limit, limit,

View File

@ -3,25 +3,26 @@ import { Args, Context, ID, Query } from '@nestjs/graphql'
import { Roles } from '../../decorator/roles.decorator' import { Roles } from '../../decorator/roles.decorator'
import { UserModel } from '../../dto/user/user.model' import { UserModel } from '../../dto/user/user.model'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { UserByIdPipe } from '../../pipe/user/user.by.id.pipe' import { UserService } from '../../service/user/user.service'
import { IdService } from '../../service/id.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@Injectable() @Injectable()
export class UserQuery { export class UserQuery {
constructor( constructor(
private readonly idService: IdService, private readonly userService: UserService,
) { ) {
} }
@Query(() => UserModel) @Query(() => UserModel)
@Roles('admin') @Roles('admin')
public getUserById( public async getUserById(
@Args('id', {type: () => ID}, UserByIdPipe) user: UserEntity, @Args('id', {type: () => ID}) id: string,
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): UserModel { ): Promise<UserModel> {
const user = await this.userService.findById(id)
cache.add(cache.getCacheKey(UserEntity.name, user.id), user) cache.add(cache.getCacheKey(UserEntity.name, user.id), user)
return new UserModel(this.idService.encode(user.id), user) return new UserModel(user)
} }
} }

View File

@ -21,7 +21,7 @@ export class UserResolver {
@Context('cache') cache: ContextCache, @Context('cache') cache: ContextCache,
): Promise<string[]> { ): Promise<string[]> {
return this.returnFieldForSuperuser( return this.returnFieldForSuperuser(
await cache.get<UserEntity>(cache.getCacheKey(UserEntity.name, parent._id)), await cache.get<UserEntity>(cache.getCacheKey(UserEntity.name, parent.id)),
user, user,
c => c.roles c => c.roles
) )

View File

@ -5,7 +5,6 @@ import { User } from '../../decorator/user.decorator'
import { UserModel } from '../../dto/user/user.model' import { UserModel } from '../../dto/user/user.model'
import { UserUpdateInput } from '../../dto/user/user.update.input' import { UserUpdateInput } from '../../dto/user/user.update.input'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { IdService } from '../../service/id.service'
import { UserService } from '../../service/user/user.service' import { UserService } from '../../service/user/user.service'
import { UserUpdateService } from '../../service/user/user.update.service' import { UserUpdateService } from '../../service/user/user.update.service'
import { ContextCache } from '../context.cache' import { ContextCache } from '../context.cache'
@ -15,7 +14,6 @@ export class UserUpdateMutation {
constructor( constructor(
private readonly updateService: UserUpdateService, private readonly updateService: UserUpdateService,
private readonly userService: UserService, private readonly userService: UserService,
private readonly idService: IdService,
) { ) {
} }
@ -30,12 +28,12 @@ export class UserUpdateMutation {
throw new Error('cannot update your own user') throw new Error('cannot update your own user')
} }
const user = await this.userService.findById(this.idService.decode(input.id)) const user = await this.userService.findById(input.id)
await this.updateService.update(user, input) await this.updateService.update(user, input)
cache.add(cache.getCacheKey(UserEntity.name, user.id), user) cache.add(cache.getCacheKey(UserEntity.name, user.id), user)
return new UserModel(this.idService.encode(user.id), user) return new UserModel(user)
} }
} }

View File

@ -3,16 +3,13 @@ import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm' import { Repository } from 'typeorm'
import { FormCreateInput } from '../../dto/form/form.create.input' import { FormCreateInput } from '../../dto/form/form.create.input'
import { FormEntity } from '../../entity/form.entity' import { FormEntity } from '../../entity/form.entity'
import { PageEntity } from '../../entity/page.entity'
import { UserEntity } from '../../entity/user.entity' import { UserEntity } from '../../entity/user.entity'
import { FormPageCreateService } from './form.page.create.service'
@Injectable() @Injectable()
export class FormCreateService { export class FormCreateService {
constructor( constructor(
@InjectRepository(FormEntity) @InjectRepository(FormEntity)
private readonly formRepository: Repository<FormEntity>, private readonly formRepository: Repository<FormEntity>
private readonly formPageCreateService: FormPageCreateService,
) { ) {
} }
@ -26,14 +23,9 @@ export class FormCreateService {
form.language = input.language || 'en' form.language = input.language || 'en'
form.design.layout = input.layout form.design.layout = input.layout
form.endPage = this.formPageCreateService.create(input.endPage)
form.startPage = this.formPageCreateService.create(input.startPage)
form.admin = admin form.admin = admin
return await this.formRepository.save(form) return await this.formRepository.save(form)
} }
} }

View File

@ -14,13 +14,15 @@ export class FormDeleteService {
) { ) {
} }
async delete(id: number): Promise<void> { async delete(id: string): Promise<void> {
await this.submissionRepository.delete({ await this.submissionRepository.createQueryBuilder('s')
form: new FormEntity({ id }), .delete()
}) .where('s.form = :form', { form: id })
.execute()
await this.formRepository.delete({ await this.formRepository.createQueryBuilder('f')
id, .delete()
}) .where('f.id = :form', { form: id })
.execute()
} }
} }

View File

@ -1,44 +0,0 @@
import { Injectable } from '@nestjs/common'
import { PageInput } from '../../dto/form/page.input'
import { PageButtonEntity } from '../../entity/page.button.entity'
import { PageEntity } from '../../entity/page.entity'
import { IdService } from '../id.service'
@Injectable()
export class FormPageCreateService {
constructor(
private readonly idService: IdService,
) {
}
public create(input: PageInput): PageEntity {
const page = new PageEntity()
page.show = Boolean(input?.show)
page.buttons = []
if (!input) {
return page
}
page.title = input.title
page.buttonText = input.buttonText
page.paragraph = input.paragraph
if (input.buttons !== undefined) {
page.buttons = input.buttons.map(buttonInput => {
const button = new PageButtonEntity()
button.page = page
button.url = buttonInput.url
button.action = buttonInput.action
button.text = buttonInput.text
button.color = buttonInput.color
button.bgColor = buttonInput.bgColor
button.activeColor = buttonInput.activeColor
return button
})
}
return page
}
}

View File

@ -1,73 +0,0 @@
import { Injectable } from '@nestjs/common'
import { PageInput } from '../../dto/form/page.input'
import { PageButtonEntity } from '../../entity/page.button.entity'
import { PageEntity } from '../../entity/page.entity'
import { IdService } from '../id.service'
@Injectable()
export class FormPageUpdateService {
constructor(
private readonly idService: IdService,
) {
}
public update(page: PageEntity, input: PageInput): PageEntity {
if (!page) {
page = new PageEntity()
page.show = false
}
if (input.show !== undefined) {
page.show = input.show
}
if (input.title !== undefined) {
page.title = input.title
}
if (input.paragraph !== undefined) {
page.paragraph = input.paragraph
}
if (input.buttonText !== undefined) {
page.buttonText = input.buttonText
}
if (input.buttons !== undefined) {
page.buttons = input.buttons.map(buttonInput => {
const entity = this.findByIdInList(
page?.buttons,
buttonInput.id,
new PageButtonEntity()
)
entity.page = page
entity.url = buttonInput.url
entity.action = buttonInput.action
entity.text = buttonInput.text
entity.color = buttonInput.color
entity.bgColor = buttonInput.bgColor
entity.activeColor = buttonInput.activeColor
return entity
})
}
return page
}
private findByIdInList<T extends { id: number }>(list: T[], id: string, fallback: T): T {
if (!list || /^NEW-/.test(id)) {
return fallback
}
const found = list.find((value) => value.id === this.idService.decode(id))
if (found) {
return found
}
return fallback
}
}

View File

@ -10,8 +10,6 @@ import { FormHookEntity } from '../../entity/form.hook.entity'
import { FormNotificationEntity } from '../../entity/form.notification.entity' import { FormNotificationEntity } from '../../entity/form.notification.entity'
import { PageButtonEntity } from '../../entity/page.button.entity' import { PageButtonEntity } from '../../entity/page.button.entity'
import { PageEntity } from '../../entity/page.entity' import { PageEntity } from '../../entity/page.entity'
import { IdService } from '../id.service'
import { FormPageUpdateService } from './form.page.update.service'
@Injectable() @Injectable()
export class FormUpdateService { export class FormUpdateService {
@ -22,8 +20,6 @@ export class FormUpdateService {
private readonly formFieldRepository: Repository<FormFieldEntity>, private readonly formFieldRepository: Repository<FormFieldEntity>,
@InjectRepository(FormHookEntity) @InjectRepository(FormHookEntity)
private readonly formHookRepository: Repository<FormHookEntity>, private readonly formHookRepository: Repository<FormHookEntity>,
private readonly idService: IdService,
private readonly pageService: FormPageUpdateService,
) { ) {
} }
@ -263,11 +259,87 @@ export class FormUpdateService {
*/ */
if (input.startPage !== undefined) { if (input.startPage !== undefined) {
form.startPage = this.pageService.update(form.startPage, input.startPage) if (!form.startPage) {
form.startPage = new PageEntity()
form.startPage.show = false
}
if (input.startPage.show !== undefined) {
form.startPage.show = input.startPage.show
}
if (input.startPage.title !== undefined) {
form.startPage.title = input.startPage.title
}
if (input.startPage.paragraph !== undefined) {
form.startPage.paragraph = input.startPage.paragraph
}
if (input.startPage.buttonText !== undefined) {
form.startPage.buttonText = input.startPage.buttonText
}
if (input.startPage.buttons !== undefined) {
form.startPage.buttons = input.startPage.buttons.map(buttonInput => {
const entity = this.findByIdInList(
form.startPage?.buttons,
buttonInput.id,
new PageButtonEntity()
)
entity.page = form.startPage
entity.url = buttonInput.url
entity.action = buttonInput.action
entity.text = buttonInput.text
entity.color = buttonInput.color
entity.bgColor = buttonInput.bgColor
entity.activeColor = buttonInput.activeColor
return entity
})
}
} }
if (input.endPage !== undefined) { if (input.endPage !== undefined) {
form.endPage = this.pageService.update(form.endPage, input.endPage) if (!form.endPage) {
form.endPage = new PageEntity()
form.endPage.show = false
}
if (input.endPage.show !== undefined) {
form.endPage.show = input.endPage.show
}
if (input.endPage.title !== undefined) {
form.endPage.title = input.endPage.title
}
if (input.endPage.paragraph !== undefined) {
form.endPage.paragraph = input.endPage.paragraph
}
if (input.endPage.buttonText !== undefined) {
form.endPage.buttonText = input.endPage.buttonText
}
if (input.endPage.buttons !== undefined) {
form.endPage.buttons = input.endPage.buttons.map(buttonInput => {
const entity = this.findByIdInList(
form.endPage?.buttons,
buttonInput.id,
new PageButtonEntity()
)
entity.page = form.endPage
entity.url = buttonInput.url
entity.action = buttonInput.action
entity.text = buttonInput.text
entity.color = buttonInput.color
entity.bgColor = buttonInput.bgColor
entity.activeColor = buttonInput.activeColor
return entity
})
}
} }
await this.formRepository.save(form) await this.formRepository.save(form)
@ -275,12 +347,12 @@ export class FormUpdateService {
return form return form
} }
private findByIdInList<T extends { id: number }>(list: T[], id: string, fallback: T): T { private findByIdInList<T>(list: T[], id: string, fallback: T): T {
if (!list || /^NEW-/.test(id) || !id) { if (!list) {
return fallback return fallback
} }
const found = list.find((value) => value.id === this.idService.decode(id)) const found = list.find((value: any) => String(value.id) === String(id))
if (found) { if (found) {
return found return found

View File

@ -1,8 +1,6 @@
import { FormCreateService } from './form.create.service' import { FormCreateService } from './form.create.service'
import { FormDeleteService } from './form.delete.service' import { FormDeleteService } from './form.delete.service'
import { FormFieldService } from './form.field.service' import { FormFieldService } from './form.field.service'
import { FormPageCreateService } from './form.page.create.service'
import { FormPageUpdateService } from './form.page.update.service'
import { FormService } from './form.service' import { FormService } from './form.service'
import { FormStatisticService } from './form.statistic.service' import { FormStatisticService } from './form.statistic.service'
import { FormUpdateService } from './form.update.service' import { FormUpdateService } from './form.update.service'
@ -11,8 +9,6 @@ export const formServices = [
FormCreateService, FormCreateService,
FormDeleteService, FormDeleteService,
FormFieldService, FormFieldService,
FormPageCreateService,
FormPageUpdateService,
FormService, FormService,
FormStatisticService, FormStatisticService,
FormUpdateService, FormUpdateService,

View File

@ -1,32 +0,0 @@
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import Hashids from 'hashids'
@Injectable()
export class IdService {
private readonly hashids: Hashids
constructor(
readonly config: ConfigService
) {
this.hashids = new Hashids(config.get('SECRET_KEY'), 6)
}
public encode(id: number): string {
return this.hashids.encode([ id ])
}
public decode(raw: string): number {
if (!this.hashids.isValidId(raw)) {
throw new Error('invalid id passed')
}
const results: number[] = this.hashids.decode(raw) as number[]
if (results[0] === undefined) {
throw new Error('invalid id passed')
}
return results[0]
}
}

View File

@ -5,7 +5,6 @@ import Redis from 'ioredis'
import { PinoLogger } from 'nestjs-pino' import { PinoLogger } from 'nestjs-pino'
import { authServices } from './auth' import { authServices } from './auth'
import { formServices } from './form' import { formServices } from './form'
import { IdService } from './id.service'
import { InstallationMetricsService } from './installation.metrics.service' import { InstallationMetricsService } from './installation.metrics.service'
import { MailService } from './mail.service' import { MailService } from './mail.service'
import { profileServices } from './profile' import { profileServices } from './profile'
@ -45,5 +44,4 @@ export const services = [
}) })
}, },
}, },
IdService,
] ]

View File

@ -25,12 +25,12 @@ export class SubmissionNotificationService {
try { try {
const to = this.getEmail( const to = this.getEmail(
submission, submission,
notification.toField?.id, notification.toField.id,
notification.toEmail notification.toEmail
) )
const from = this.getEmail( const from = this.getEmail(
submission, submission,
notification.fromField?.id, notification.fromField.id,
notification.fromEmail notification.fromEmail
) )
@ -73,18 +73,14 @@ export class SubmissionNotificationService {
} }
private getEmail(submission: SubmissionEntity, fieldId: number, fallback: string): string { private getEmail(submission: SubmissionEntity, fieldId: number, fallback: string): string {
if (!fieldId) {
return fallback
}
const data = submission.fields.find(field => field.fieldId === fieldId)?.content const data = submission.fields.find(field => field.fieldId === fieldId)?.content
if (!data) { if (!data) {
return fallback return fallback
} }
if (typeof data === 'string') { if (typeof data.value === 'string') {
return data return data.value
} }
return fallback return fallback

View File

@ -58,7 +58,7 @@ export class SubmissionService {
return await qb.getManyAndCount() return await qb.getManyAndCount()
} }
async findById(id: number): Promise<SubmissionEntity> { async findById(id: string): Promise<SubmissionEntity> {
const submission = await this.submissionRepository.findOne(id); const submission = await this.submissionRepository.findOne(id);
if (!submission) { if (!submission) {

View File

@ -7,7 +7,6 @@ import { Repository } from 'typeorm'
import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.field.input' import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.field.input'
import { SubmissionEntity } from '../../entity/submission.entity' import { SubmissionEntity } from '../../entity/submission.entity'
import { SubmissionFieldContent, SubmissionFieldEntity } from '../../entity/submission.field.entity' import { SubmissionFieldContent, SubmissionFieldEntity } from '../../entity/submission.field.entity'
import { IdService } from '../id.service'
import { SubmissionHookService } from './submission.hook.service' import { SubmissionHookService } from './submission.hook.service'
import { SubmissionNotificationService } from './submission.notification.service' import { SubmissionNotificationService } from './submission.notification.service'
@ -20,16 +19,13 @@ export class SubmissionSetFieldService {
private readonly submissionFieldRepository: Repository<SubmissionFieldEntity>, private readonly submissionFieldRepository: Repository<SubmissionFieldEntity>,
private readonly webHook: SubmissionHookService, private readonly webHook: SubmissionHookService,
private readonly notifications: SubmissionNotificationService, private readonly notifications: SubmissionNotificationService,
private readonly idService: IdService,
private readonly logger: PinoLogger, private readonly logger: PinoLogger,
) { ) {
logger.setContext(this.constructor.name) logger.setContext(this.constructor.name)
} }
async saveField(submission: SubmissionEntity, input: SubmissionSetFieldInput): Promise<void> { async saveField(submission: SubmissionEntity, input: SubmissionSetFieldInput): Promise<void> {
const formFieldId = this.idService.decode(input.field) let field = submission.fields.find(field => field.field.id.toString() === input.field)
let field = submission.fields.find(field => field.field.id === formFieldId)
submission.timeElapsed = dayjs().diff(dayjs(submission.created), 'second') submission.timeElapsed = dayjs().diff(dayjs(submission.created), 'second')
@ -42,7 +38,7 @@ export class SubmissionSetFieldService {
field = new SubmissionFieldEntity() field = new SubmissionFieldEntity()
field.submission = submission field.submission = submission
field.field = submission.form.fields.find(field => field.id === formFieldId) field.field = submission.form.fields.find(field => field.id.toString() === input.field)
field.type = field.field.type field.type = field.field.type
field.content = this.parseData(field, input.data) field.content = this.parseData(field, input.data)
@ -88,7 +84,7 @@ export class SubmissionSetFieldService {
field: SubmissionFieldEntity, field: SubmissionFieldEntity,
data: string data: string
): SubmissionFieldContent { ): SubmissionFieldContent {
let raw: SubmissionFieldContent let raw: { [key: string]: unknown }
const context = { const context = {
field: field.fieldId, field: field.fieldId,
@ -96,49 +92,21 @@ export class SubmissionSetFieldService {
} }
try { try {
raw = JSON.parse(data) as SubmissionFieldContent raw = JSON.parse(data) as { [key: string]: unknown }
} catch (e) { } catch (e) {
this.logger.warn(context, 'received invalid data for field') this.logger.warn(context, 'received invalid data for field')
return { value: null } return { value: null }
} }
if (Array.isArray(raw)) { if (typeof raw !== 'object' || Array.isArray(raw)) {
return raw.map((row: unknown, index) => { this.logger.warn(context, 'only object supported for data')
switch (typeof row) { return { value: null }
case 'number':
case 'string':
case 'boolean':
case 'undefined':
return row
}
if (row === null) {
return row
}
this.logger.warn({
...context,
path: `${index}`,
}, 'invalid data in array')
valid = false
return null
})
}
if (
[
'number',
'string',
'boolean',
'undefined',
].includes(typeof raw)
) {
return raw
} }
// now ensure data structure // now ensure data structure
const result = {} const result = {
value: null,
}
let valid = true let valid = true
@ -179,48 +147,6 @@ export class SubmissionSetFieldService {
return return
} }
if (typeof value === 'object') {
result[String(key)] = {}
for (const subKey of Object.keys(value)) {
const subValue = raw[String(key)][String(subKey)]
switch (typeof subValue) {
case 'number':
case 'string':
case 'boolean':
result[String(key)][String(subKey)] = subValue
return
}
if (Array.isArray(subValue)) {
result[String(key)][String(subKey)] = subValue.map((row: unknown, index) => {
switch (typeof row) {
case 'number':
case 'string':
case 'boolean':
case 'undefined':
return row
}
if (row === null) {
return row
}
this.logger.warn({
...context,
path: `${key}/${subKey}/${index}`,
}, 'invalid data in array')
valid = false
return null
})
return
}
}
}
this.logger.warn({ this.logger.warn({
...context, ...context,
path: String(key), path: String(key),

View File

@ -11,7 +11,7 @@ export class UserDeleteService {
) { ) {
} }
async delete(id: number): Promise<void> { async delete(id: string): Promise<void> {
await this.userRepository.delete(id) await this.userRepository.delete(id)
} }
} }

View File

@ -26,7 +26,7 @@ export class UserService {
return await qb.getManyAndCount() return await qb.getManyAndCount()
} }
async findById(id: number): Promise<UserEntity> { async findById(id: string): Promise<UserEntity> {
const user = await this.userRepository.findOne(id); const user = await this.userRepository.findOne(id);
if (!user) { if (!user) {

View File

@ -16,15 +16,15 @@ export class UserUpdateService {
} }
async update(user: UserEntity, input: UserUpdateInput): Promise<UserEntity> { async update(user: UserEntity, input: UserUpdateInput): Promise<UserEntity> {
if (this.shouldUpdate(input, user, 'firstName')) { if (input.firstName !== undefined) {
user.firstName = input.firstName user.firstName = input.firstName
} }
if (this.shouldUpdate(input, user, 'lastName')) { if (input.lastName !== undefined) {
user.lastName = input.lastName user.lastName = input.lastName
} }
if (this.shouldUpdate(input, user, 'email')) { if (input.email !== undefined) {
user.email = input.email user.email = input.email
user.emailVerified = false user.emailVerified = false
// TODO request email verification // TODO request email verification
@ -34,15 +34,15 @@ export class UserUpdateService {
} }
} }
if (this.shouldUpdate(input, user, 'username')) { if (input.username !== undefined) {
user.username = input.username user.username = input.username
} }
if (this.shouldUpdate(input, user, 'roles')) { if (input.roles !== undefined) {
user.roles = input.roles as rolesType user.roles = input.roles as rolesType
} }
if (this.shouldUpdate(input, user, 'language')) { if (input.language !== undefined) {
user.language = input.language user.language = input.language
} }
@ -54,16 +54,4 @@ export class UserUpdateService {
return user return user
} }
private shouldUpdate(
input: UserUpdateInput,
user: UserEntity,
property: keyof UserUpdateInput
): boolean {
if (input[property] === undefined) {
return false
}
return input[property] == user[property]
}
} }

354
yarn.lock
View File

@ -532,11 +532,6 @@
minimatch "^3.0.4" minimatch "^3.0.4"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@graphql-tools/merge@8.2.2": "@graphql-tools/merge@8.2.2":
version "8.2.2" version "8.2.2"
resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.2.tgz#433566c662a33f5a9c3cc5f3ce3753fb0019477a" resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.2.tgz#433566c662a33f5a9c3cc5f3ce3753fb0019477a"
@ -1075,22 +1070,6 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@npmcli/fs@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.0.tgz#f2a21c28386e299d1a9fae8051d35ad180e33109"
integrity sha512-DmfBvNXGaetMxj9LTp8NAN9vEidXURrf5ZTslQzEAi/6GbW+4yjaLFQc6Tue5cpZ9Frlk4OBo/Snf1Bh/S7qTQ==
dependencies:
"@gar/promisify" "^1.1.3"
semver "^7.3.5"
"@npmcli/move-file@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674"
integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==
dependencies:
mkdirp "^1.0.4"
rimraf "^3.0.2"
"@nuxtjs/opencollective@0.3.2": "@nuxtjs/opencollective@0.3.2":
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c"
@ -1185,11 +1164,6 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
"@tsconfig/node10@^1.0.7": "@tsconfig/node10@^1.0.7":
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
@ -1382,7 +1356,7 @@
dependencies: dependencies:
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
"@types/jest@^27.4.1": "@types/jest@27.4.1":
version "27.4.1" version "27.4.1"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
@ -1863,23 +1837,6 @@ agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2:
dependencies: dependencies:
debug "4" debug "4"
agentkeepalive@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==
dependencies:
debug "^4.1.0"
depd "^1.1.2"
humanize-ms "^1.2.1"
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
dependencies:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ajv-formats@2.1.1: ajv-formats@2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
@ -2107,14 +2064,6 @@ are-we-there-yet@^2.0.0:
delegates "^1.0.0" delegates "^1.0.0"
readable-stream "^3.6.0" readable-stream "^3.6.0"
are-we-there-yet@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d"
integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
are-we-there-yet@~1.1.2: are-we-there-yet@~1.1.2:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
@ -2487,30 +2436,6 @@ bytes@3.1.1:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a"
integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==
cacache@^16.0.2:
version "16.0.3"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.0.3.tgz#0b6314bde969bd4098b03a5f90a351e8a1483f48"
integrity sha512-eC7wYodNCVb97kuHGk5P+xZsvUJHkhSEOyNwkenqQPAsOtrTjvWOE5vSPNBpz9d8X3acIf6w2Ub5s4rvOCTs4g==
dependencies:
"@npmcli/fs" "^2.1.0"
"@npmcli/move-file" "^1.1.2"
chownr "^2.0.0"
fs-minipass "^2.1.0"
glob "^7.2.0"
infer-owner "^1.0.4"
lru-cache "^7.7.1"
minipass "^3.1.6"
minipass-collect "^1.0.2"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
mkdirp "^1.0.4"
p-map "^4.0.0"
promise-inflight "^1.0.1"
rimraf "^3.0.2"
ssri "^8.0.1"
tar "^6.1.11"
unique-filename "^1.1.1"
call-bind@^1.0.0, call-bind@^1.0.2: call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@ -2720,11 +2645,6 @@ clean-css@^4.2.1:
dependencies: dependencies:
source-map "~0.6.0" source-map "~0.6.0"
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
cli-cursor@^3.1.0: cli-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
@ -2821,7 +2741,7 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-support@^1.1.2, color-support@^1.1.3: color-support@^1.1.2:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
@ -3173,7 +3093,7 @@ denque@^2.0.1:
resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a" resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a"
integrity sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ== integrity sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==
depd@^1.1.2, depd@~1.1.2: depd@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
@ -3430,13 +3350,6 @@ encoding-japanese@1.0.30:
resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-1.0.30.tgz#537c4d62881767925d601acb4c79fb14db81703a" resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-1.0.30.tgz#537c4d62881767925d601acb4c79fb14db81703a"
integrity sha512-bd/DFLAoJetvv7ar/KIpE3CNO8wEuyrt9Xuw6nSMiZ+Vrz/Q21BPsMHvARL2Wz6IKHKXgb+DWZqtRg1vql9cBg== integrity sha512-bd/DFLAoJetvv7ar/KIpE3CNO8wEuyrt9Xuw6nSMiZ+Vrz/Q21BPsMHvARL2Wz6IKHKXgb+DWZqtRg1vql9cBg==
encoding@^0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"
end-of-stream@^1.1.0, end-of-stream@^1.4.1: end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -3462,16 +3375,6 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
err-code@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
error-ex@^1.3.1: error-ex@^1.3.1:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@ -4156,7 +4059,7 @@ fs-minipass@^1.2.7:
dependencies: dependencies:
minipass "^2.6.0" minipass "^2.6.0"
fs-minipass@^2.0.0, fs-minipass@^2.1.0: fs-minipass@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
@ -4221,20 +4124,6 @@ gauge@^3.0.0:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
wide-align "^1.1.2" wide-align "^1.1.2"
gauge@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.3.tgz#286cf105c1962c659f0963058fb05116c1b82d3f"
integrity sha512-ICw1DhAwMtb22rYFwEHgJcx1JCwJGv3x6G0OQUq56Nge+H4Q8JEwr8iveS0XFlsUNSI67F5ffMGK25bK4Pmskw==
dependencies:
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.3"
console-control-strings "^1.1.0"
has-unicode "^2.0.1"
signal-exit "^3.0.7"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.5"
gauge@~2.7.3: gauge@~2.7.3:
version "2.7.4" version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@ -4350,7 +4239,7 @@ glob@7.1.7:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
@ -4391,7 +4280,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
graceful-fs@^4.2.6, graceful-fs@^4.2.9: graceful-fs@^4.2.9:
version "4.2.9" version "4.2.9"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
@ -4503,11 +4392,6 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
hashids@^2.2.10:
version "2.2.10"
resolved "https://registry.yarnpkg.com/hashids/-/hashids-2.2.10.tgz#82f45538cf03ce73e31b78d1abe78d287cf760c4"
integrity sha512-nXnYums7F8B5Y+GSThutLPlKMaamW1yjWNZVt0WModiJfdjaDZHnhYTWblS+h1OoBx3yjwiBwxldPP3nIbFSSA==
he@1.2.0, he@^1.2.0: he@1.2.0, he@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@ -4618,11 +4502,6 @@ htmlparser2@^6.1.0:
domutils "^2.5.2" domutils "^2.5.2"
entities "^2.0.0" entities "^2.0.0"
http-cache-semantics@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-errors@1.8.1: http-errors@1.8.1:
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
@ -4643,15 +4522,6 @@ http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1:
agent-base "6" agent-base "6"
debug "4" debug "4"
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
dependencies:
"@tootallnate/once" "2"
agent-base "6"
debug "4"
http-signature@~1.2.0: http-signature@~1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@ -4679,13 +4549,6 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=
dependencies:
ms "^2.0.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -4700,7 +4563,7 @@ iconv-lite@0.6.2:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3.0.0" safer-buffer ">= 2.1.2 < 3.0.0"
iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: iconv-lite@0.6.3, iconv-lite@^0.6.3:
version "0.6.3" version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@ -4750,16 +4613,6 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
infer-owner@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -4986,11 +4839,6 @@ is-interactive@^1.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
is-lambda@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=
is-negative-zero@^2.0.1: is-negative-zero@^2.0.1:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
@ -6075,11 +5923,6 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
lru-cache@^7.7.1:
version "7.7.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.7.1.tgz#03d2846b1ad2dcc7931a9340b8711d9798fcb0c6"
integrity sha512-cRffBiTW8s73eH4aTXqBcTLU0xQnwGV3/imttRHGWCrbergmnK4D6JXQd8qin5z43HnDwRI+o7mVW0LEB+tpAw==
macos-release@^2.5.0: macos-release@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2"
@ -6128,28 +5971,6 @@ make-error@1.x, make-error@^1.1.1:
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
make-fetch-happen@^10.0.3:
version "10.1.0"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.1.0.tgz#7232ef7002a635c04d4deac325df144f21871408"
integrity sha512-HeP4QlkadP/Op+hE+Une1070kcyN85FshQObku3/rmzRh4zDcKXA19d2L3AQR6UoaX3uZmhSOpTLH15b1vOFvQ==
dependencies:
agentkeepalive "^4.2.1"
cacache "^16.0.2"
http-cache-semantics "^4.1.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-lambda "^1.0.1"
lru-cache "^7.7.1"
minipass "^3.1.6"
minipass-collect "^1.0.2"
minipass-fetch "^2.0.3"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
negotiator "^0.6.3"
promise-retry "^2.0.1"
socks-proxy-agent "^6.1.1"
ssri "^8.0.1"
makeerror@1.0.12: makeerror@1.0.12:
version "1.0.12" version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@ -6253,45 +6074,6 @@ minimist@1.2.5, minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass-collect@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
dependencies:
minipass "^3.0.0"
minipass-fetch@^2.0.3:
version "2.1.0"
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.0.tgz#ca1754a5f857a3be99a9271277246ac0b44c3ff8"
integrity sha512-H9U4UVBGXEyyWJnqYDCLp1PwD8XIkJ4akNHp1aGVI+2Ym7wQMlxDKi4IB4JbmyU+pl9pEs/cVrK6cOuvmbK4Sg==
dependencies:
minipass "^3.1.6"
minipass-sized "^1.0.3"
minizlib "^2.1.2"
optionalDependencies:
encoding "^0.1.13"
minipass-flush@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
dependencies:
minipass "^3.0.0"
minipass-pipeline@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
dependencies:
minipass "^3.0.0"
minipass-sized@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70"
integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
dependencies:
minipass "^3.0.0"
minipass@^2.6.0, minipass@^2.9.0: minipass@^2.6.0, minipass@^2.9.0:
version "2.9.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
@ -6300,7 +6082,7 @@ minipass@^2.6.0, minipass@^2.9.0:
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
yallist "^3.0.0" yallist "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: minipass@^3.0.0:
version "3.1.6" version "3.1.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
@ -6314,7 +6096,7 @@ minizlib@^1.3.3:
dependencies: dependencies:
minipass "^2.9.0" minipass "^2.9.0"
minizlib@^2.1.1, minizlib@^2.1.2: minizlib@^2.1.1:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
@ -6686,7 +6468,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.0.0, ms@^2.1.1: ms@2.1.3, ms@^2.1.1:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@ -6769,11 +6551,6 @@ negotiator@0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
negotiator@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
neo-async@^2.6.0, neo-async@^2.6.2: neo-async@^2.6.0, neo-async@^2.6.2:
version "2.6.2" version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
@ -6816,14 +6593,7 @@ node-emoji@1.11.0:
dependencies: dependencies:
lodash "^4.17.21" lodash "^4.17.21"
node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.5: node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.5, node-fetch@^2.6.7:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.7:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -6848,22 +6618,6 @@ node-gyp@3.x:
tar "^2.0.0" tar "^2.0.0"
which "1" which "1"
node-gyp@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.0.0.tgz#e1da2067427f3eb5bb56820cb62bc6b1e4bd2089"
integrity sha512-Ma6p4s+XCTPxCuAMrOA/IJRmVy16R8Sdhtwl4PrCr7IBlj4cPawF0vg/l7nOT1jPbuNS7lIRJpBSvVsXwEZuzw==
dependencies:
env-paths "^2.2.0"
glob "^7.1.4"
graceful-fs "^4.2.6"
make-fetch-happen "^10.0.3"
nopt "^5.0.0"
npmlog "^6.0.0"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.2"
which "^2.0.2"
node-int64@^0.4.0: node-int64@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@ -6975,16 +6729,6 @@ npmlog@^5.0.1:
gauge "^3.0.0" gauge "^3.0.0"
set-blocking "^2.0.0" set-blocking "^2.0.0"
npmlog@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17"
integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==
dependencies:
are-we-there-yet "^3.0.0"
console-control-strings "^1.1.0"
gauge "^4.0.0"
set-blocking "^2.0.0"
nth-check@^2.0.1: nth-check@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
@ -7198,13 +6942,6 @@ p-map@^2.1.0:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
dependencies:
aggregate-error "^3.0.0"
p-try@^1.0.0: p-try@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
@ -7592,19 +7329,6 @@ process-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
promise-retry@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
dependencies:
err-code "^2.0.2"
retry "^0.12.0"
promise@^7.0.1: promise@^7.0.1:
version "7.3.1" version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
@ -8074,11 +7798,6 @@ retry@0.13.1:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
reusify@^1.0.4: reusify@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@ -8326,11 +8045,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
sisteransi@^1.0.5: sisteransi@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@ -8346,7 +8060,7 @@ slick@^1.12.2:
resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7"
integrity sha1-vQSN23TefRymkV+qSldXCzVQwtc= integrity sha1-vQSN23TefRymkV+qSldXCzVQwtc=
smart-buffer@^4.1.0, smart-buffer@^4.2.0: smart-buffer@^4.1.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
@ -8360,15 +8074,6 @@ socks-proxy-agent@5, socks-proxy-agent@^5.0.0:
debug "4" debug "4"
socks "^2.3.3" socks "^2.3.3"
socks-proxy-agent@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
dependencies:
agent-base "^6.0.2"
debug "^4.3.1"
socks "^2.6.1"
socks@^2.3.3: socks@^2.3.3:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e"
@ -8377,14 +8082,6 @@ socks@^2.3.3:
ip "^1.1.5" ip "^1.1.5"
smart-buffer "^4.1.0" smart-buffer "^4.1.0"
socks@^2.6.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a"
integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==
dependencies:
ip "^1.1.5"
smart-buffer "^4.2.0"
sonic-boom@^2.2.0, sonic-boom@^2.2.1: sonic-boom@^2.2.0, sonic-boom@^2.2.1:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.4.2.tgz#34c0965b1a498abedaaca794c752d190f74b5e8f" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.4.2.tgz#34c0965b1a498abedaaca794c752d190f74b5e8f"
@ -8465,13 +8162,6 @@ sshpk@^1.7.0:
safer-buffer "^2.0.2" safer-buffer "^2.0.2"
tweetnacl "~0.14.0" tweetnacl "~0.14.0"
ssri@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
dependencies:
minipass "^3.1.1"
stack-utils@^2.0.3: stack-utils@^2.0.3:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5"
@ -8745,7 +8435,7 @@ tar@^4:
safe-buffer "^5.2.1" safe-buffer "^5.2.1"
yallist "^3.1.1" yallist "^3.1.1"
tar@^6.1.11, tar@^6.1.2: tar@^6.1.11:
version "6.1.11" version "6.1.11"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
@ -9103,20 +8793,6 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2" has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2" which-boxed-primitive "^1.0.2"
unique-filename@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
dependencies:
unique-slug "^2.0.0"
unique-slug@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
dependencies:
imurmurhash "^0.1.4"
universalify@^0.1.0, universalify@^0.1.2: universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@ -9367,14 +9043,14 @@ which@1:
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
which@^2.0.1, which@^2.0.2: which@^2.0.1:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: wide-align@^1.1.0, wide-align@^1.1.2:
version "1.1.5" version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==