diff --git a/apps/demo/pages/_app.tsx b/apps/demo/pages/_app.tsx index 6b3970196c..42f6f9e3b5 100644 --- a/apps/demo/pages/_app.tsx +++ b/apps/demo/pages/_app.tsx @@ -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"; diff --git a/packages/api/.eslintrc.js b/packages/api/.eslintrc.js new file mode 100644 index 0000000000..bcf4aad3a3 --- /dev/null +++ b/packages/api/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["formbricks"], +}; diff --git a/packages/api/README.md b/packages/api/README.md new file mode 100644 index 0000000000..b6e2fbc266 --- /dev/null +++ b/packages/api/README.md @@ -0,0 +1,55 @@ +# @formbricks/api - API Wrapper for Formbricks + +## Installation + +```bash +npm install @formbricks/api +``` + +## Usage + +
+Create API Client + +```ts +import { FormbricksAPI, EnvironmentId } from "@formbricks/api"; + +const api = new FormbricksAPI({ + apiHost: "http://localhost:3000", + environmentId: "clgwh8maj0005n2f66pwzev3r" as EnvironmentId, +}); +``` + +
+ +> **Note** +> All of the following methods return a `Result` from the `@formbricks/errors` package. + +
+Create a new response + +```ts +const response = await api.createResponse({ + surveyId: "......" as SurveyId, + personId: "......" as PersonId, + data: { + questionId: "response", + }, +}); +``` + +
+ +
+Update an existing response + +```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", + }, +}); +``` + +
diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 0000000000..b8ec958850 --- /dev/null +++ b/packages/api/package.json @@ -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" + } +} diff --git a/packages/api/src/dtos/index.ts b/packages/api/src/dtos/index.ts new file mode 100644 index 0000000000..8818ea007e --- /dev/null +++ b/packages/api/src/dtos/index.ts @@ -0,0 +1 @@ +export * from "./responses"; diff --git a/packages/api/src/dtos/responses.ts b/packages/api/src/dtos/responses.ts new file mode 100644 index 0000000000..c593294c01 --- /dev/null +++ b/packages/api/src/dtos/responses.ts @@ -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 { + createdAt: Date; + updatedAt: Date; +} diff --git a/packages/api/src/endpoints/response.ts b/packages/api/src/endpoints/response.ts new file mode 100644 index 0000000000..50ed488ae1 --- /dev/null +++ b/packages/api/src/endpoints/response.ts @@ -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> => { + const result = await request( + `/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( + `/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); +}; diff --git a/packages/api/src/errors.ts b/packages/api/src/errors.ts new file mode 100644 index 0000000000..b58906ea3a --- /dev/null +++ b/packages/api/src/errors.ts @@ -0,0 +1,6 @@ +export type NetworkError = { + code: "network_error"; + message: string; + status: number; + url: URL; +}; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts new file mode 100644 index 0000000000..9ab1508e44 --- /dev/null +++ b/packages/api/src/index.ts @@ -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"; diff --git a/packages/api/src/lib.ts b/packages/api/src/lib.ts new file mode 100644 index 0000000000..56c22d31ad --- /dev/null +++ b/packages/api/src/lib.ts @@ -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 + ): Promise> { + return this.runWithEnvironmentId(createResponse, options); + } + + async updateResponse( + options: Omit + ): Promise> { + 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( + fn: (request: RequestFn, options: Options) => Promise>, + options: Omit + ): Promise> { + const newOptions = { environmentId: this.environmentId, ...options } as Options; + + return fn(this.request, newOptions); + } + + private async request( + path: string, + data: Data, + options?: RequestInit + ): Promise> { + 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); + } +} diff --git a/packages/api/src/types.d.ts b/packages/api/src/types.d.ts new file mode 100644 index 0000000000..8149c03a10 --- /dev/null +++ b/packages/api/src/types.d.ts @@ -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 & { __brand: B }; + +export type EnvironmentId = Brand; +export type SurveyId = Brand; +export type PersonId = Brand; +export type ResponseId = Brand; + +export type KeyValueData = { [key: string]: string | number | string[] | number[] | undefined }; + +export type RequestFn = ( + path: string, + data: Data, + options?: RequestInit +) => Promise>; + +// 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[]; + } +} \ No newline at end of file diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json new file mode 100644 index 0000000000..431acb1f6a --- /dev/null +++ b/packages/api/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@formbricks/tsconfig/js-library.json", + "include": ["**/*.ts", "tsup.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/api/tsup.config.ts b/packages/api/tsup.config.ts new file mode 100644 index 0000000000..7f7ecd034b --- /dev/null +++ b/packages/api/tsup.config.ts @@ -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, +}); diff --git a/packages/errors/.eslintrc.js b/packages/errors/.eslintrc.js new file mode 100644 index 0000000000..bcf4aad3a3 --- /dev/null +++ b/packages/errors/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["formbricks"], +}; diff --git a/packages/errors/README.md b/packages/errors/README.md new file mode 100644 index 0000000000..06061623df --- /dev/null +++ b/packages/errors/README.md @@ -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 => { + /* 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) diff --git a/packages/errors/package.json b/packages/errors/package.json new file mode 100644 index 0000000000..7f731f41a1 --- /dev/null +++ b/packages/errors/package.json @@ -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" + } +} diff --git a/packages/errors/src/functions.ts b/packages/errors/src/functions.ts new file mode 100644 index 0000000000..bb36248643 --- /dev/null +++ b/packages/errors/src/functions.ts @@ -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): Result} A new function that takes in a `Result` object and returns a new `Result` object. + * + * @example + * const divideByTwo = (num: number): Result => { + * if (num === 0) { + * return { ok: false, error: "Cannot divide zero" }; + * } + * + * return { ok: true, data: num / 2 }; + * } + * + * const wrappedDivideByTwo = wrap(divideByTwo); + * + * const result1: Result = { ok: true, data: 10 }; + * const result2: Result = { ok: false, error: "Invalid input" }; + * const result3: Result = { 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 = + (fn: (value: T) => R) => + (result: Result): Result => + 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} 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 => { + * 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( + result: Result, + 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} 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 = wrappedDivideByTwo(10); // { ok: true, data: 5 } + * const result2: Result = wrappedDivideByTwo(0); // { ok: false, error: Error("Cannot divide zero") } + */ +export const wrapThrows = + (fn: (...args: A) => T): ((...args: A) => Result) => + (...args: A): Result => { + try { + return { + ok: true, + data: fn(...args), + }; + } catch (error: any) { + return { + ok: false, + error, + }; + } + }; diff --git a/packages/errors/src/index.ts b/packages/errors/src/index.ts new file mode 100644 index 0000000000..192a290220 --- /dev/null +++ b/packages/errors/src/index.ts @@ -0,0 +1,2 @@ +export * from "./functions"; +export * from "./result"; diff --git a/packages/errors/src/result.ts b/packages/errors/src/result.ts new file mode 100644 index 0000000000..c9827566ee --- /dev/null +++ b/packages/errors/src/result.ts @@ -0,0 +1,10 @@ +export type Result = { ok: true; data: T } | { ok: false; error: E }; + +export const ok = (data: T): Result => ({ ok: true, data }); + +export const okVoid = (): Result => ({ ok: true, data: undefined }); + +export const err = (error: E): Result => ({ + ok: false, + error, +}); diff --git a/packages/errors/tsconfig.json b/packages/errors/tsconfig.json new file mode 100644 index 0000000000..68efa55c17 --- /dev/null +++ b/packages/errors/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@formbricks/tsconfig/node16.json", + "include": ["**/*.ts", "tsup.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/errors/tsup.config.ts b/packages/errors/tsup.config.ts new file mode 100644 index 0000000000..7f7ecd034b --- /dev/null +++ b/packages/errors/tsup.config.ts @@ -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, +}); diff --git a/packages/eslint-config-formbricks/package.json b/packages/eslint-config-formbricks/package.json index 31f0bab3b9..2e3e08c191 100644 --- a/packages/eslint-config-formbricks/package.json +++ b/packages/eslint-config-formbricks/package.json @@ -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" diff --git a/packages/js/package.json b/packages/js/package.json index f8e0c6bcce..a2f968de3c 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -45,6 +45,10 @@ }, "author": "Formbricks ", "license": "MIT", + "dependencies": { + "@formbricks/api": "workspace:*", + "@formbricks/errors": "workspace:*" + }, "devDependencies": { "@formbricks/types": "workspace:*", "@types/enzyme": "^3.10.13", diff --git a/packages/js/src/index.ts b/packages/js/src/index.ts index aac3ea859d..edf461256e 100644 --- a/packages/js/src/index.ts +++ b/packages/js/src/index.ts @@ -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 }; diff --git a/packages/js/src/lib/api.ts b/packages/js/src/lib/api.ts new file mode 100644 index 0000000000..bfcd86b9c5 --- /dev/null +++ b/packages/js/src/lib/api.ts @@ -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, + }); +}; diff --git a/packages/types/js.ts b/packages/types/js.ts index 1a77a06538..9947ee9312 100644 --- a/packages/types/js.ts +++ b/packages/types/js.ts @@ -3,7 +3,7 @@ import { ThankYouCard } from "./surveys"; export interface ResponseCreateRequest { surveyId: string; - personId: string; + personId?: string; response: { finished?: boolean; data: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 267ea3840b..edce323296 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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'}