create: api wrapper & errors package (#262)

* add @formbricks/api (api abstraction layer) and @formbricks/errors package to monorepo
* use @formbricks/api in @formbricks/js to expose an api endpoint

---------

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Midka
2023-06-01 15:02:53 +03:00
committed by GitHub
parent dbe7f138b6
commit 7fa2a260e8
27 changed files with 701 additions and 200 deletions

View File

@@ -1,4 +1,4 @@
import formbricks from "@formbricks/js";
import formbricks, { PersonId, SurveyId } from "@formbricks/js";
import type { AppProps } from "next/app";
import { useRouter } from "next/router";
import { useEffect } from "react";

View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["formbricks"],
};

55
packages/api/README.md Normal file
View File

@@ -0,0 +1,55 @@
# @formbricks/api - API Wrapper for Formbricks
## Installation
```bash
npm install @formbricks/api
```
## Usage
<details>
<summary>Create API Client</summary>
```ts
import { FormbricksAPI, EnvironmentId } from "@formbricks/api";
const api = new FormbricksAPI({
apiHost: "http://localhost:3000",
environmentId: "clgwh8maj0005n2f66pwzev3r" as EnvironmentId,
});
```
</details>
> **Note**
> All of the following methods return a `Result` from the `@formbricks/errors` package.
<details>
<summary>Create a new response</summary>
```ts
const response = await api.createResponse({
surveyId: "......" as SurveyId,
personId: "......" as PersonId,
data: {
questionId: "response",
},
});
```
</details>
<details>
<summary>Update an existing response</summary>
```ts
const response = await api.updateResponse({
responseId: "......" as ResponseId, // If you pass response.value.id from createResponse, you dont need 'as ResponseId'
data: {
questionId: "response",
},
});
```
</details>

29
packages/api/package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "@formbricks/api",
"version": "1.0.0",
"license": "MIT",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"sideEffects": false,
"files": [
"dist/**"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint ./src --fix",
"clean": "rimraf .turbo node_modules dist"
},
"dependencies": {
"@formbricks/errors": "workspace:*"
},
"devDependencies": {
"@formbricks/types": "workspace:*",
"@formbricks/tsconfig": "workspace:*",
"eslint": "^8.39.0",
"eslint-config-formbricks": "workspace:*",
"rimraf": "^4.4.1",
"tsup": "^6.7.0"
}
}

View File

@@ -0,0 +1 @@
export * from "./responses";

View File

@@ -0,0 +1,24 @@
import { KeyValueData, PersonId, ResponseId, SurveyId } from "../types";
export interface CreateResponseResponse {
id: ResponseId;
}
export interface UpdateResponseResponse {
id: ResponseId;
createdAt: string;
updatedAt: string;
finished: boolean;
surveyId: SurveyId;
personId: PersonId;
data: KeyValueData;
meta: {}; //TODO: figure out what this is
userAttributes: string[]; //TODO: figure out what this is
tags: string[]; //TODO: figure out what this is
}
export interface UpdateResponseResponseFormatted
extends Omit<UpdateResponseResponse, "createdAt" | "updatedAt"> {
createdAt: Date;
updatedAt: Date;
}

View File

@@ -0,0 +1,69 @@
import { Result, ok } from "@formbricks/errors";
import { ResponseCreateRequest, ResponseUpdateRequest } from "@formbricks/types/js";
import {
CreateResponseResponse,
UpdateResponseResponse,
UpdateResponseResponseFormatted,
} from "../dtos/responses";
import { NetworkError } from "../errors";
import { EnvironmentId, KeyValueData, PersonId, RequestFn, ResponseId, SurveyId } from "../types";
export interface CreateResponseOptions {
environmentId: EnvironmentId;
surveyId: SurveyId;
personId?: PersonId;
data: KeyValueData;
}
export const createResponse = async (
request: RequestFn,
options: CreateResponseOptions
): Promise<Result<CreateResponseResponse, NetworkError>> => {
const result = await request<CreateResponseResponse, any, ResponseCreateRequest>(
`/api/v1/client/environments/${options.environmentId}/responses`,
{
surveyId: options.surveyId,
personId: options.personId,
response: {
data: options.data,
finished: false,
},
},
{ method: "POST" }
);
return result;
};
export interface UpdateResponseOptions {
environmentId: EnvironmentId;
data: KeyValueData;
responseId: ResponseId;
finished?: boolean;
}
export const updateResponse = async (request: RequestFn, options: UpdateResponseOptions) => {
const result = await request<UpdateResponseResponse, any, ResponseUpdateRequest>(
`/api/v1/client/environments/${options.environmentId}/responses/${options.responseId}`,
{
response: {
data: options.data,
finished: options.finished || false,
},
},
{
method: "PUT",
}
);
if (result.ok === false) return result;
// convert timestamps to Dates
const newResponse: UpdateResponseResponseFormatted = {
...result.data,
createdAt: new Date(result.data.createdAt),
updatedAt: new Date(result.data.updatedAt),
};
return ok(newResponse);
};

View File

@@ -0,0 +1,6 @@
export type NetworkError = {
code: "network_error";
message: string;
status: number;
url: URL;
};

View File

@@ -0,0 +1,6 @@
export * from "./dtos/";
export * from "./errors";
export * from "./lib";
export { FormbricksAPI as default } from "./lib";
// do not export RequestFn or Brand, they are internal
export type { EnvironmentId, KeyValueData, PersonId, ResponseId, SurveyId } from "./types";

85
packages/api/src/lib.ts Normal file
View File

@@ -0,0 +1,85 @@
import { Result, err, ok, wrapThrows } from "@formbricks/errors";
import { CreateResponseResponse, UpdateResponseResponseFormatted } from "./dtos/responses";
import { NetworkError } from "./errors";
import {
CreateResponseOptions,
UpdateResponseOptions,
createResponse,
updateResponse,
} from "./endpoints/response";
import { EnvironmentId, RequestFn } from "./types";
export interface FormbricksAPIOptions {
apiHost?: string;
environmentId: EnvironmentId;
}
export class FormbricksAPI {
private readonly baseUrl: string;
private readonly environmentId: EnvironmentId;
constructor(options: FormbricksAPIOptions) {
this.baseUrl = options.apiHost || "https://app.formbricks.com";
this.environmentId = options.environmentId;
this.request = this.request.bind(this);
}
async createResponse(
options: Omit<CreateResponseOptions, "environmentId">
): Promise<Result<CreateResponseResponse, NetworkError>> {
return this.runWithEnvironmentId(createResponse, options);
}
async updateResponse(
options: Omit<UpdateResponseOptions, "environmentId">
): Promise<Result<UpdateResponseResponseFormatted, NetworkError>> {
return this.runWithEnvironmentId(updateResponse, options);
}
/*
This was added to reduce code duplication
It checks that the function passed has the environmentId in the Options type
and automatically adds it to the options
*/
private runWithEnvironmentId<T, E, Options extends { environmentId: EnvironmentId }>(
fn: (request: RequestFn, options: Options) => Promise<Result<T, E>>,
options: Omit<Options, "environmentId">
): Promise<Result<T, E>> {
const newOptions = { environmentId: this.environmentId, ...options } as Options;
return fn(this.request, newOptions);
}
private async request<T = any, E = any, Data = any>(
path: string,
data: Data,
options?: RequestInit
): Promise<Result<T, E | NetworkError | Error>> {
const url = new URL(path, this.baseUrl);
const headers: HeadersInit = {
"Content-Type": "application/json",
};
const body = JSON.stringify(data);
const res = wrapThrows(fetch)(url, { headers, body, ...options });
if (res.ok === false) return err(res.error);
const response = await res.data;
const resJson = await response.json();
if (!response.ok) {
return err({
code: "network_error",
message: response.statusText,
status: response.status,
url,
});
}
return ok(resJson.data as T);
}
}

27
packages/api/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,27 @@
import { Result } from "@formbricks/errors";
import { NetworkError } from "./errors";
// by using Brand, we can check that you can't pass to an environmentId a surveyId
type Brand<T, B> = T & { __brand: B };
export type EnvironmentId = Brand<string, "EnvironmentId">;
export type SurveyId = Brand<string, "SurveyId">;
export type PersonId = Brand<string, "PersonId">;
export type ResponseId = Brand<string, "ResponseId">;
export type KeyValueData = { [key: string]: string | number | string[] | number[] | undefined };
export type RequestFn = <T = any, E = any, Data = any>(
path: string,
data: Data,
options?: RequestInit
) => Promise<Result<T, E | NetworkError | Error>>;
// https://github.com/formbricks/formbricks/blob/fbfc80dd4ed5d768f0c549e179fd1aa10edc400a/apps/web/lib/api/response.ts
export interface ApiErrorResponse {
code: string;
message: string;
details: {
[key: string]: string | string[] | number | number[] | boolean | boolean[];
}
}

View File

@@ -0,0 +1,5 @@
{
"extends": "@formbricks/tsconfig/js-library.json",
"include": ["**/*.ts", "tsup.config.ts"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,13 @@
import { defineConfig } from "tsup";
const isProduction = process.env.NODE_ENV === "production";
export default defineConfig({
clean: true,
dts: true,
splitting: true,
format: ["cjs", "esm"],
entry: ["src/index.ts"],
minify: isProduction,
sourcemap: true,
});

View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["formbricks"],
};

57
packages/errors/README.md Normal file
View File

@@ -0,0 +1,57 @@
# @formbricks/errors
> Error handling for @formbricks packages
## Installation
```bash
npm install @formbricks/errors
```
## Usage
```ts
import { Result, ok, err, okVoid } from "@formbricks/errors";
type CustomError = {
code: "custom_error";
message: string;
};
type AnotherCustomError = {
code: "another_custom_error";
message: string;
anotherField: number;
};
type SuccessType = {
id: string;
};
const test = (): Result<SuccessType, CustomError | AnotherCustomError> => {
/* There are 4 ways to return a Result from this function */
// return ok({ id: '123' })
// return err({ code: 'custom_error', message: 'Custom error message' })
// return err({ code: 'another_custom_error', message: 'Another custom error message', anotherField: 123 })
/*
If SuccessType is void
*/
// return okVoid()
};
const result = test();
if (result.ok === true) {
console.log(result.value.id); // you have full type safety here
} else if (result.error.code === "custom_error") {
console.log(result.error.message); // you have full type safety here
} else if (result.error.code === "another_custom_error") {
console.log(result.error.anotherField); // you have full type safety here
console.log(result.error.message);
}
```
## Inspiration
- [Rust Result](https://doc.rust-lang.org/std/result/enum.Result.html)
- [true-myth](https://github.com/true-myth/true-myth)

View File

@@ -0,0 +1,25 @@
{
"name": "@formbricks/errors",
"version": "1.0.0",
"license": "MIT",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"sideEffects": false,
"files": [
"dist/**"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint ./src --fix",
"clean": "rimraf .turbo node_modules dist"
},
"devDependencies": {
"@formbricks/tsconfig": "workspace:*",
"eslint": "^8.39.0",
"eslint-config-formbricks": "workspace:*",
"rimraf": "^4.4.1",
"tsup": "^6.7.0"
}
}

View File

@@ -0,0 +1,116 @@
import { Result } from "./result";
/**
* Applies the given function `fn` to the data property of the input `result` object
* and returns a new `Result` object with the transformed data property.
*
* @template T The type of the input data.
* @template R The type of the output data.
*
* @param {function(value: T): R} fn The function to apply to the data property of the input `result` object.
* @returns {function(result: Result<T>): Result<R>} A new function that takes in a `Result<T>` object and returns a new `Result<R>` object.
*
* @example
* const divideByTwo = (num: number): Result<number> => {
* if (num === 0) {
* return { ok: false, error: "Cannot divide zero" };
* }
*
* return { ok: true, data: num / 2 };
* }
*
* const wrappedDivideByTwo = wrap(divideByTwo);
*
* const result1: Result<number> = { ok: true, data: 10 };
* const result2: Result<number> = { ok: false, error: "Invalid input" };
* const result3: Result<number> = { ok: true, data: 0 };
*
* console.log(wrappedDivideByTwo(result1)); // { ok: true, data: 5 }
* console.log(wrappedDivideByTwo(result2)); // { ok: false, error: "Invalid input" }
* console.log(wrappedDivideByTwo(result3)); // { ok: false, error: "Cannot divide zero" }
*/
export const wrap =
<T, R>(fn: (value: T) => R) =>
(result: Result<T>): Result<R> =>
result.ok === true ? { ok: true, data: fn(result.data) } : result;
/**
* Matches the given `result` object against its `ok` property and invokes the `onSuccess` function
* if `ok` is `true`, or the `onError` function if `ok` is `false`. Returns the result of the invoked function. Match a Result object and run a function depending on the result.
*
* @template TSuccess - Type of the success value
* @template TError - Type of the error value
* @template TReturn - Type of the return value
*
* @param {Result<TSuccess, TError>} result The `Result` object to match against.
* @param {(value: TSuccess) => TReturn} onSuccess The function to invoke if `result.ok` is `true`.
* @param {(error: TError) => TReturn} onError The function to invoke if `result.ok` is `false`.
*
* @returns {TReturn} The result of the invoked function.
*
* @example
* const test = (): Result<string, Error> => {
* return err(new Error("error happened"));
* }
*
* const result = test();
*
* match(result, (value) => {
* console.log(value); // never run with this example
* }, (error) => {
* console.log(error); // Error: error happened
* });
*/
export function match<TSuccess, TError, TReturn>(
result: Result<TSuccess, TError>,
onSuccess: (value: TSuccess) => TReturn,
onError: (error: TError) => TReturn
): TReturn {
if (result.ok === true) {
return onSuccess(result.data);
}
return onError(result.error);
}
/**
* Wraps a function `fn` that may throw an error into a new function that returns a `Result` object.
* If the wrapped function throws an error, the returned `Result` object will have an `ok` property of `false`
* and an `error` property containing the thrown error. Otherwise, the returned `Result` object will have an
* `ok` property of `true` and a `data` property containing the result of the wrapped function.
*
* @template T The type of the result value.
* @template A An array of the types of the arguments expected by the wrapped function.
*
* @param {(...args: A) => T} fn The function to wrap.
* @returns {(...args: A) => Result<T>} A new function that returns a `Result` object.
*
* @example
* function divideByTwo(num: number): number {
* if (num === 0) {
* throw new Error("Cannot divide zero");
* }
* return num / 2;
* }
*
* const wrappedDivideByTwo = wrapThrows(divideByTwo);
*
* const result1: Result<number> = wrappedDivideByTwo(10); // { ok: true, data: 5 }
* const result2: Result<number> = wrappedDivideByTwo(0); // { ok: false, error: Error("Cannot divide zero") }
*/
export const wrapThrows =
<T, A extends any[]>(fn: (...args: A) => T): ((...args: A) => Result<T>) =>
(...args: A): Result<T> => {
try {
return {
ok: true,
data: fn(...args),
};
} catch (error: any) {
return {
ok: false,
error,
};
}
};

View File

@@ -0,0 +1,2 @@
export * from "./functions";
export * from "./result";

View File

@@ -0,0 +1,10 @@
export type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };
export const ok = <T, E>(data: T): Result<T, E> => ({ ok: true, data });
export const okVoid = <E>(): Result<void, E> => ({ ok: true, data: undefined });
export const err = <E = Error>(error: E): Result<never, E> => ({
ok: false,
error,
});

View File

@@ -0,0 +1,5 @@
{
"extends": "@formbricks/tsconfig/node16.json",
"include": ["**/*.ts", "tsup.config.ts"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,13 @@
import { defineConfig } from "tsup";
const isProduction = process.env.NODE_ENV === "production";
export default defineConfig({
clean: true,
dts: true,
splitting: true,
format: ["cjs", "esm"],
entry: ["src/index.ts"],
minify: isProduction,
sourcemap: true,
});

View File

@@ -8,8 +8,8 @@
"clean": "rimraf node_modules .turbo"
},
"dependencies": {
"eslint": "^8.37.0",
"eslint-config-next": "^13.2.4",
"eslint": "^8.39.0",
"eslint-config-next": "^13.3.1",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-react": "7.32.2",
"eslint-config-turbo": "latest"

View File

@@ -45,6 +45,10 @@
},
"author": "Formbricks <hola@formbricks.com>",
"license": "MIT",
"dependencies": {
"@formbricks/api": "workspace:*",
"@formbricks/errors": "workspace:*"
},
"devDependencies": {
"@formbricks/types": "workspace:*",
"@types/enzyme": "^3.10.13",

View File

@@ -1,4 +1,5 @@
import type { InitConfig } from "../../types/js";
import { getApi } from "./lib/api";
import { CommandQueue } from "./lib/commandQueue";
import { ErrorHandler } from "./lib/errors";
import { trackEvent } from "./lib/event";
@@ -8,6 +9,8 @@ import { checkPageUrl } from "./lib/noCodeEvents";
import { resetPerson, setPersonAttribute, setPersonUserId } from "./lib/person";
import { refreshSettings } from "./lib/settings";
export type { EnvironmentId, KeyValueData, PersonId, ResponseId, SurveyId } from "@formbricks/api";
const logger = Logger.getInstance();
logger.debug("Create command queue");
@@ -46,6 +49,16 @@ const registerRouteChange = (): void => {
queue.add(true, checkPageUrl);
};
const formbricks = { init, setUserId, setEmail, setAttribute, track, logout, refresh, registerRouteChange };
const formbricks = {
init,
setUserId,
setEmail,
setAttribute,
track,
logout,
refresh,
registerRouteChange,
getApi,
};
export default formbricks;
export { formbricks as default };

View File

@@ -0,0 +1,16 @@
import { EnvironmentId, FormbricksAPI } from "@formbricks/api";
import { Config } from "./config";
export const getApi = (): FormbricksAPI => {
const config = Config.getInstance();
const { environmentId, apiHost } = config.get();
if (!environmentId || !apiHost) {
throw new Error("formbricks.init() must be called before getApi()");
}
return new FormbricksAPI({
apiHost,
environmentId: environmentId as EnvironmentId,
});
};

View File

@@ -3,7 +3,7 @@ import { ThankYouCard } from "./surveys";
export interface ResponseCreateRequest {
surveyId: string;
personId: string;
personId?: string;
response: {
finished?: boolean;
data: {

300
pnpm-lock.yaml generated
View File

@@ -350,6 +350,31 @@ importers:
specifier: ^1.0.5
version: 1.0.5(tailwindcss@3.3.2)
packages/api:
dependencies:
'@formbricks/errors':
specifier: workspace:*
version: link:../errors
devDependencies:
'@formbricks/tsconfig':
specifier: workspace:*
version: link:../tsconfig
'@formbricks/types':
specifier: workspace:*
version: link:../types
eslint:
specifier: ^8.39.0
version: 8.41.0
eslint-config-formbricks:
specifier: workspace:*
version: link:../eslint-config-formbricks
rimraf:
specifier: ^4.4.1
version: 4.4.1
tsup:
specifier: ^6.7.0
version: 6.7.0(postcss@8.4.23)(typescript@5.0.4)
packages/database:
dependencies:
'@formbricks/types':
@@ -418,29 +443,54 @@ importers:
specifier: ^4.9.4
version: 4.9.5
packages/errors:
devDependencies:
'@formbricks/tsconfig':
specifier: workspace:*
version: link:../tsconfig
eslint:
specifier: ^8.39.0
version: 8.41.0
eslint-config-formbricks:
specifier: workspace:*
version: link:../eslint-config-formbricks
rimraf:
specifier: ^4.4.1
version: 4.4.1
tsup:
specifier: ^6.7.0
version: 6.7.0(postcss@8.4.23)(typescript@5.0.4)
packages/eslint-config-formbricks:
dependencies:
eslint:
specifier: ^8.37.0
version: 8.37.0
specifier: ^8.39.0
version: 8.41.0
eslint-config-next:
specifier: ^13.2.4
version: 13.2.4(eslint@8.37.0)(typescript@5.0.4)
specifier: ^13.3.1
version: 13.4.3(eslint@8.41.0)(typescript@5.0.4)
eslint-config-prettier:
specifier: ^8.8.0
version: 8.8.0(eslint@8.37.0)
version: 8.8.0(eslint@8.41.0)
eslint-config-turbo:
specifier: latest
version: 1.8.8(eslint@8.37.0)
version: 1.8.8(eslint@8.41.0)
eslint-plugin-react:
specifier: 7.32.2
version: 7.32.2(eslint@8.37.0)
version: 7.32.2(eslint@8.41.0)
devDependencies:
rimraf:
specifier: ^5.0.0
version: 5.0.0
packages/js:
dependencies:
'@formbricks/api':
specifier: workspace:*
version: link:../api
'@formbricks/errors':
specifier: workspace:*
version: link:../errors
devDependencies:
'@formbricks/types':
specifier: workspace:*
@@ -3461,12 +3511,6 @@ packages:
resolution: {integrity: sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ==}
dev: false
/@next/eslint-plugin-next@13.2.4:
resolution: {integrity: sha512-ck1lI+7r1mMJpqLNa3LJ5pxCfOB1lfJncKmRJeJxcJqcngaFwylreLP7da6Rrjr6u2gVRTfmnkSkjc80IiQCwQ==}
dependencies:
glob: 7.1.7
dev: false
/@next/eslint-plugin-next@13.4.3:
resolution: {integrity: sha512-5B0uOnh7wyUY9vNNdIA6NUvWozhrZaTMZOzdirYAefqD0ZBK5C/h3+KMYdCKrR7JrXGvVpWnHtv54b3dCzwICA==}
dependencies:
@@ -3881,7 +3925,7 @@ packages:
open: 8.4.0
picocolors: 1.0.0
tiny-glob: 0.2.9
tslib: 2.4.1
tslib: 2.5.2
dev: false
/@polka/url@1.0.0-next.21:
@@ -4825,7 +4869,7 @@ packages:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.3.1(postcss@8.4.21)
tailwindcss: 3.3.1(postcss@8.4.22)
dev: true
/@tailwindcss/forms@0.5.3(tailwindcss@3.3.2):
@@ -5377,26 +5421,6 @@ packages:
- typescript
dev: true
/@typescript-eslint/parser@5.55.0(eslint@8.37.0)(typescript@5.0.4):
resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 5.55.0
'@typescript-eslint/types': 5.55.0
'@typescript-eslint/typescript-estree': 5.55.0(typescript@5.0.4)
debug: 4.3.4
eslint: 8.37.0
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: false
/@typescript-eslint/parser@5.59.2(eslint@8.40.0)(typescript@5.0.4):
resolution: {integrity: sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5445,14 +5469,6 @@ packages:
'@typescript-eslint/visitor-keys': 5.54.0
dev: true
/@typescript-eslint/scope-manager@5.55.0:
resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.55.0
'@typescript-eslint/visitor-keys': 5.55.0
dev: false
/@typescript-eslint/scope-manager@5.59.2:
resolution: {integrity: sha512-dB1v7ROySwQWKqQ8rEWcdbTsFjh2G0vn8KUyvTXdPoyzSL6lLGkiXEV5CvpJsEe9xIdKV+8Zqb7wif2issoOFA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5485,11 +5501,6 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@typescript-eslint/types@5.55.0:
resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
/@typescript-eslint/types@5.59.2:
resolution: {integrity: sha512-LbJ/HqoVs2XTGq5shkiKaNTuVv5tTejdHgfdjqRUGdYhjW1crm/M7og2jhVskMt8/4wS3T1+PfFvL1K3wqYj4w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5515,27 +5526,6 @@ packages:
- supports-color
dev: true
/@typescript-eslint/typescript-estree@5.55.0(typescript@5.0.4):
resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 5.55.0
'@typescript-eslint/visitor-keys': 5.55.0
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
tsutils: 3.21.0(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: false
/@typescript-eslint/typescript-estree@5.59.2(typescript@5.0.4):
resolution: {integrity: sha512-+j4SmbwVmZsQ9jEyBMgpuBD0rKwi9RxRpjX71Brr73RsYnEr3Lt5QZ624Bxphp8HUkSKfqGnPJp1kA5nl0Sh7Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5604,14 +5594,6 @@ packages:
eslint-visitor-keys: 3.4.1
dev: true
/@typescript-eslint/visitor-keys@5.55.0:
resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.55.0
eslint-visitor-keys: 3.4.1
dev: false
/@typescript-eslint/visitor-keys@5.59.2:
resolution: {integrity: sha512-EEpsO8m3RASrKAHI9jpavNv9NlEUebV4qmF1OWxSTtKSFBpC1NCmWazDQHFivRf0O1DV11BA645yrLEVQ0/Lig==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -9272,31 +9254,6 @@ packages:
source-map: 0.6.1
dev: true
/eslint-config-next@13.2.4(eslint@8.37.0)(typescript@5.0.4):
resolution: {integrity: sha512-lunIBhsoeqw6/Lfkd6zPt25w1bn0znLA/JCL+au1HoEpSb4/PpsOYsYtgV/q+YPsoKIOzFyU5xnb04iZnXjUvg==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
typescript: '>=3.3.1'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@next/eslint-plugin-next': 13.2.4
'@rushstack/eslint-patch': 1.2.0
'@typescript-eslint/parser': 5.55.0(eslint@8.37.0)(typescript@5.0.4)
eslint: 8.37.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.26.0)(eslint@8.37.0)
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0)
eslint-plugin-jsx-a11y: 6.6.1(eslint@8.37.0)
eslint-plugin-react: 7.32.2(eslint@8.37.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.37.0)
typescript: 5.0.4
transitivePeerDependencies:
- eslint-import-resolver-webpack
- supports-color
dev: false
/eslint-config-next@13.4.3(eslint@8.41.0)(typescript@5.0.4):
resolution: {integrity: sha512-1lXwdFi29fKxzeugof/TUE7lpHyJQt5+U4LaUHyvQfHjvsWO77vFNicJv5sX6k0VDVSbnfz0lw+avxI+CinbMg==}
peerDependencies:
@@ -9312,7 +9269,7 @@ packages:
eslint: 8.41.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.26.0)(eslint@8.41.0)
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0)
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@3.5.2)(eslint@8.41.0)
eslint-plugin-jsx-a11y: 6.6.1(eslint@8.41.0)
eslint-plugin-react: 7.32.2(eslint@8.41.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.41.0)
@@ -9344,22 +9301,22 @@ packages:
- typescript
dev: true
/eslint-config-prettier@8.8.0(eslint@8.37.0):
/eslint-config-prettier@8.8.0(eslint@8.41.0):
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.37.0
eslint: 8.41.0
dev: false
/eslint-config-turbo@1.8.8(eslint@8.37.0):
/eslint-config-turbo@1.8.8(eslint@8.41.0):
resolution: {integrity: sha512-+yT22sHOT5iC1sbBXfLIdXfbZuiv9bAyOXsxTxFCWelTeFFnANqmuKB3x274CFvf7WRuZ/vYP/VMjzU9xnFnxA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
eslint: 8.37.0
eslint-plugin-turbo: 1.8.8(eslint@8.37.0)
eslint: 8.41.0
eslint-plugin-turbo: 1.8.8(eslint@8.41.0)
dev: false
/eslint-import-resolver-node@0.3.6:
@@ -9371,26 +9328,6 @@ packages:
- supports-color
dev: false
/eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.26.0)(eslint@8.37.0):
resolution: {integrity: sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
eslint: '*'
eslint-plugin-import: '*'
dependencies:
debug: 4.3.4
enhanced-resolve: 5.12.0
eslint: 8.37.0
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0)
get-tsconfig: 4.4.0
globby: 13.1.2
is-core-module: 2.11.0
is-glob: 4.0.3
synckit: 0.8.4
transitivePeerDependencies:
- supports-color
dev: false
/eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.26.0)(eslint@8.41.0):
resolution: {integrity: sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -9401,7 +9338,7 @@ packages:
debug: 4.3.4
enhanced-resolve: 5.12.0
eslint: 8.41.0
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0)
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@3.5.2)(eslint@8.41.0)
get-tsconfig: 4.4.0
globby: 13.1.2
is-core-module: 2.11.0
@@ -9411,7 +9348,7 @@ packages:
- supports-color
dev: false
/eslint-module-utils@2.7.4(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-node@0.3.6)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0):
/eslint-module-utils@2.7.4(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.6)(eslint-import-resolver-typescript@3.5.2)(eslint@8.41.0):
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
engines: {node: '>=4'}
peerDependencies:
@@ -9432,11 +9369,11 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.55.0(eslint@8.37.0)(typescript@5.0.4)
'@typescript-eslint/parser': 5.59.2(eslint@8.41.0)(typescript@5.0.4)
debug: 3.2.7
eslint: 8.37.0
eslint: 8.41.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.26.0)(eslint@8.37.0)
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.26.0)(eslint@8.41.0)
transitivePeerDependencies:
- supports-color
dev: false
@@ -9457,7 +9394,7 @@ packages:
semver: 7.3.8
dev: true
/eslint-plugin-import@2.26.0(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0):
/eslint-plugin-import@2.26.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@3.5.2)(eslint@8.41.0):
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
engines: {node: '>=4'}
peerDependencies:
@@ -9467,14 +9404,14 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.55.0(eslint@8.37.0)(typescript@5.0.4)
'@typescript-eslint/parser': 5.59.2(eslint@8.41.0)(typescript@5.0.4)
array-includes: 3.1.6
array.prototype.flat: 1.3.1
debug: 2.6.9
doctrine: 2.1.0
eslint: 8.37.0
eslint: 8.41.0
eslint-import-resolver-node: 0.3.6
eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-node@0.3.6)(eslint-import-resolver-typescript@3.5.2)(eslint@8.37.0)
eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.6)(eslint-import-resolver-typescript@3.5.2)(eslint@8.41.0)
has: 1.0.3
is-core-module: 2.11.0
is-glob: 4.0.3
@@ -9510,28 +9447,6 @@ packages:
- typescript
dev: true
/eslint-plugin-jsx-a11y@6.6.1(eslint@8.37.0):
resolution: {integrity: sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==}
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
'@babel/runtime': 7.21.0
aria-query: 4.2.2
array-includes: 3.1.6
ast-types-flow: 0.0.7
axe-core: 4.5.2
axobject-query: 2.2.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
eslint: 8.37.0
has: 1.0.3
jsx-ast-utils: 3.3.3
language-tags: 1.0.5
minimatch: 3.1.2
semver: 6.3.0
dev: false
/eslint-plugin-jsx-a11y@6.6.1(eslint@8.41.0):
resolution: {integrity: sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==}
engines: {node: '>=4.0'}
@@ -9554,15 +9469,6 @@ packages:
semver: 6.3.0
dev: false
/eslint-plugin-react-hooks@4.6.0(eslint@8.37.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
dependencies:
eslint: 8.37.0
dev: false
/eslint-plugin-react-hooks@4.6.0(eslint@8.40.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
@@ -9581,30 +9487,6 @@ packages:
eslint: 8.41.0
dev: false
/eslint-plugin-react@7.32.2(eslint@8.37.0):
resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
array-includes: 3.1.6
array.prototype.flatmap: 1.3.1
array.prototype.tosorted: 1.1.1
doctrine: 2.1.0
eslint: 8.37.0
estraverse: 5.3.0
jsx-ast-utils: 3.3.3
minimatch: 3.1.2
object.entries: 1.1.6
object.fromentries: 2.0.6
object.hasown: 1.1.2
object.values: 1.1.6
prop-types: 15.8.1
resolve: 2.0.0-next.4
semver: 6.3.0
string.prototype.matchall: 4.0.8
dev: false
/eslint-plugin-react@7.32.2(eslint@8.40.0):
resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==}
engines: {node: '>=4'}
@@ -9653,12 +9535,12 @@ packages:
string.prototype.matchall: 4.0.8
dev: false
/eslint-plugin-turbo@1.8.8(eslint@8.37.0):
/eslint-plugin-turbo@1.8.8(eslint@8.41.0):
resolution: {integrity: sha512-zqyTIvveOY4YU5jviDWw9GXHd4RiKmfEgwsjBrV/a965w0PpDwJgEUoSMB/C/dU310Sv9mF3DSdEjxjJLaw6rA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
eslint: 8.37.0
eslint: 8.41.0
dev: false
/eslint-scope@4.0.3:
@@ -10804,6 +10686,16 @@ packages:
once: 1.4.0
dev: true
/glob@9.3.5:
resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
fs.realpath: 1.0.0
minimatch: 8.0.4
minipass: 4.2.8
path-scurry: 1.7.0
dev: true
/global-dirs@0.1.1:
resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==}
engines: {node: '>=4'}
@@ -14024,6 +13916,13 @@ packages:
brace-expansion: 2.0.1
dev: true
/minimatch@8.0.4:
resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimatch@9.0.0:
resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -14085,6 +13984,11 @@ packages:
yallist: 4.0.0
dev: true
/minipass@4.2.8:
resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
engines: {node: '>=8'}
dev: true
/minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
@@ -17465,6 +17369,14 @@ packages:
dependencies:
glob: 7.2.3
/rimraf@4.4.1:
resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 9.3.5
dev: true
/rimraf@5.0.0:
resolution: {integrity: sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g==}
engines: {node: '>=14'}