From 1feebb8828bf06412ce9ca218d6b41d04c4910a6 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 5 Dec 2024 16:18:54 -0800 Subject: [PATCH 01/40] added generate schema --- apps/backend/scripts/generate-docs.ts | 24 ++--------------- apps/backend/scripts/generate-schema.ts | 35 +++++++++++++++++++++++++ apps/backend/src/lib/glob.tsx | 29 ++++++++++++++++++++ apps/backend/src/lib/openapi.tsx | 2 +- 4 files changed, 67 insertions(+), 23 deletions(-) create mode 100644 apps/backend/scripts/generate-schema.ts create mode 100644 apps/backend/src/lib/glob.tsx diff --git a/apps/backend/scripts/generate-docs.ts b/apps/backend/scripts/generate-docs.ts index b28a23981..4e62fce02 100644 --- a/apps/backend/scripts/generate-docs.ts +++ b/apps/backend/scripts/generate-docs.ts @@ -1,35 +1,15 @@ +import { listEndpoints } from '@/lib/glob'; import { parseOpenAPI, parseWebhookOpenAPI } from '@/lib/openapi'; -import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler'; import { webhookEvents } from '@stackframe/stack-shared/dist/interface/webhooks'; -import { HTTP_METHODS } from '@stackframe/stack-shared/dist/utils/http'; import fs from 'fs'; -import { glob } from 'glob'; -import path from 'path'; import yaml from 'yaml'; async function main() { console.log("Started docs schema generator"); for (const audience of ['client', 'server', 'admin'] as const) { - const filePathPrefix = path.resolve(process.platform === "win32" ? "apps/src/app/api/v1" : "src/app/api/v1"); - const importPathPrefix = "@/app/api/v1"; - const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")]; const openAPISchema = yaml.stringify(parseOpenAPI({ - endpoints: new Map(await Promise.all(filePaths.map(async (filePath) => { - if (!filePath.startsWith(filePathPrefix)) { - throw new Error(`Invalid file path: ${filePath}`); - } - const suffix = filePath.slice(filePathPrefix.length); - const midfix = suffix.slice(0, suffix.lastIndexOf("/route.")); - const importPath = `${importPathPrefix}${suffix}`; - const urlPath = midfix.replaceAll("[", "{").replaceAll("]", "}"); - const myModule = require(importPath); - const handlersByMethod = new Map( - HTTP_METHODS.map(method => [method, myModule[method]] as const) - .filter(([_, handler]) => isSmartRouteHandler(handler)) - ); - return [urlPath, handlersByMethod] as const; - }))), + endpoints: await listEndpoints("api/v1"), audience, })); fs.writeFileSync(`../../docs/fern/openapi/${audience}.yaml`, openAPISchema); diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts new file mode 100644 index 000000000..a303b21ec --- /dev/null +++ b/apps/backend/scripts/generate-schema.ts @@ -0,0 +1,35 @@ +import { listEndpoints } from '@/lib/glob'; + +async function main() { + console.log("Started docs schema generator"); + + const endpoints = await listEndpoints("api/v1"); + + endpoints.forEach((handlersByMethod, url) => { + console.log(url); + handlersByMethod.forEach((handler, method) => { + console.log(` ${method}`); + + if (handler.overloads.size === 1) { + console.log(' overloads: 1'); + } else { + console.log(` overloads: ${handler.overloads.size}`); + } + + for (const overload of handler.overloads.values()) { + for (const audience of ['client', 'server', 'admin'] as const) { + const schemaAudience = overload.request.describe().fields.auth?.fields?.type; + if (!schemaAudience) continue; + if ("oneOf" in schemaAudience && schemaAudience.oneOf.length > 0 && schemaAudience.oneOf.includes(audience)) { + console.log(` ${audience}`); + } + } + } + }); + }); +} + +main().catch((...args) => { + console.error(`ERROR! Could not generate schema`, ...args); + process.exit(1); +}); diff --git a/apps/backend/src/lib/glob.tsx b/apps/backend/src/lib/glob.tsx new file mode 100644 index 000000000..b2e6da348 --- /dev/null +++ b/apps/backend/src/lib/glob.tsx @@ -0,0 +1,29 @@ +import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler'; +import { HTTP_METHODS } from '@stackframe/stack-shared/dist/utils/http'; +import { glob } from 'glob'; +import path from 'path'; + +export async function listEndpoints(basePath: string) { + const filePathPrefix = path.resolve(process.platform === "win32" ? `apps/src/app/${basePath}` : `src/app/${basePath}`); + const importPathPrefix = `@/app/${basePath}`; + const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")]; + + const endpoints = new Map( + filePaths.map((filePath) => { + if (!filePath.startsWith(filePathPrefix)) { + throw new Error(`Invalid file path: ${filePath}`); + } + const suffix = filePath.slice(filePathPrefix.length); + const url = suffix.slice(0, suffix.lastIndexOf("/route.")); + const importPath = `${importPathPrefix}${suffix}`; + const myModule = require(importPath); + const handlersByMethod = new Map( + HTTP_METHODS.map(method => [method, myModule[method]] as const) + .filter(([_, handler]) => isSmartRouteHandler(handler)) + ); + return [url, handlersByMethod] as const; + }).filter((x) => x[1].size > 0) + ); + + return endpoints; +} diff --git a/apps/backend/src/lib/openapi.tsx b/apps/backend/src/lib/openapi.tsx index 7372da30e..6139c1304 100644 --- a/apps/backend/src/lib/openapi.tsx +++ b/apps/backend/src/lib/openapi.tsx @@ -102,7 +102,7 @@ function isSchemaNumberDescription(value: yup.SchemaFieldDescription): value is return value.type === 'number'; } -function isMaybeRequestSchemaForAudience(requestDescribe: yup.SchemaObjectDescription, audience: 'client' | 'server' | 'admin') { +export function isMaybeRequestSchemaForAudience(requestDescribe: yup.SchemaObjectDescription, audience: 'client' | 'server' | 'admin') { const schemaAuth = requestDescribe.fields.auth; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- yup types are wrong and claim that fields always exist if (!schemaAuth) return true; From 6ae7af6cb6351d0bd3409792503d77aca32a1f3f Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Sat, 7 Dec 2024 11:04:31 -0800 Subject: [PATCH 02/40] migration --- apps/backend/scripts/generate-schema.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index a303b21ec..f1f5f8fac 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -17,13 +17,18 @@ async function main() { } for (const overload of handler.overloads.values()) { + let audienceCount = 0; for (const audience of ['client', 'server', 'admin'] as const) { const schemaAudience = overload.request.describe().fields.auth?.fields?.type; if (!schemaAudience) continue; if ("oneOf" in schemaAudience && schemaAudience.oneOf.length > 0 && schemaAudience.oneOf.includes(audience)) { - console.log(` ${audience}`); + audienceCount++; + console.log(` ${audience}`); } } + if (handler.overloads.size !== 1 && audienceCount !== handler.overloads.size) { + throw new Error(`Expected ${handler.overloads.size} audiences, got ${audienceCount}`); + } } }); }); From 76d399f06a7f43ba8858dc3a32d96b3a63a72e39 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 9 Dec 2024 15:21:12 -0800 Subject: [PATCH 03/40] fixed migration --- apps/backend/src/lib/glob.tsx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/backend/src/lib/glob.tsx b/apps/backend/src/lib/glob.tsx index b2e6da348..c3f103bee 100644 --- a/apps/backend/src/lib/glob.tsx +++ b/apps/backend/src/lib/glob.tsx @@ -1,5 +1,6 @@ import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler'; import { HTTP_METHODS } from '@stackframe/stack-shared/dist/utils/http'; +import { typedKeys } from '@stackframe/stack-shared/dist/utils/objects'; import { glob } from 'glob'; import path from 'path'; @@ -8,22 +9,21 @@ export async function listEndpoints(basePath: string) { const importPathPrefix = `@/app/${basePath}`; const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")]; - const endpoints = new Map( - filePaths.map((filePath) => { - if (!filePath.startsWith(filePathPrefix)) { - throw new Error(`Invalid file path: ${filePath}`); - } - const suffix = filePath.slice(filePathPrefix.length); - const url = suffix.slice(0, suffix.lastIndexOf("/route.")); - const importPath = `${importPathPrefix}${suffix}`; - const myModule = require(importPath); - const handlersByMethod = new Map( - HTTP_METHODS.map(method => [method, myModule[method]] as const) - .filter(([_, handler]) => isSmartRouteHandler(handler)) - ); - return [url, handlersByMethod] as const; - }).filter((x) => x[1].size > 0) - ); + const endpoints = new Map(await Promise.all(filePaths.map(async (filePath) => { + if (!filePath.startsWith(filePathPrefix)) { + throw new Error(`Invalid file path: ${filePath}`); + } + const suffix = filePath.slice(filePathPrefix.length); + const midfix = suffix.slice(0, suffix.lastIndexOf("/route.")); + const importPath = `${importPathPrefix}${suffix}`; + const urlPath = midfix.replaceAll("[", "{").replaceAll("]", "}"); + const myModule = require(importPath); + const handlersByMethod = new Map( + typedKeys(HTTP_METHODS).map(method => [method, myModule[method]] as const) + .filter(([_, handler]) => isSmartRouteHandler(handler)) + ); + return [urlPath, handlersByMethod] as const; + }))); return endpoints; } From 0fed49a7746ebfe7492339fc13fa3f0066ae9c88 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 9 Dec 2024 16:07:34 -0800 Subject: [PATCH 04/40] updated schema gen --- apps/backend/scripts/generate-schema.ts | 55 +++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index f1f5f8fac..3d28ed45b 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -1,12 +1,13 @@ import { listEndpoints } from '@/lib/glob'; - +import fs from 'fs'; +import * as yup from 'yup'; async function main() { console.log("Started docs schema generator"); const endpoints = await listEndpoints("api/v1"); + let schemaContent = ''; endpoints.forEach((handlersByMethod, url) => { - console.log(url); handlersByMethod.forEach((handler, method) => { console.log(` ${method}`); @@ -16,22 +17,62 @@ async function main() { console.log(` overloads: ${handler.overloads.size}`); } + const audiences = new Map(); for (const overload of handler.overloads.values()) { - let audienceCount = 0; for (const audience of ['client', 'server', 'admin'] as const) { const schemaAudience = overload.request.describe().fields.auth?.fields?.type; if (!schemaAudience) continue; if ("oneOf" in schemaAudience && schemaAudience.oneOf.length > 0 && schemaAudience.oneOf.includes(audience)) { - audienceCount++; - console.log(` ${audience}`); + audiences.set(audience, overload); } } - if (handler.overloads.size !== 1 && audienceCount !== handler.overloads.size) { - throw new Error(`Expected ${handler.overloads.size} audiences, got ${audienceCount}`); + } + + if (handler.overloads.size !== 1 && audiences.size !== handler.overloads.size) { + throw new Error(`Expected ${handler.overloads.size} audiences, got ${audiences.size}. Multiple audiences other than client, server, and admin is currently not supported. You might need to manually fix this.`); + } + + if (audiences.size === 0) { + schemaContent += schemaToTypeString(handler.overloads.values().next().value.request.describe()); + } else { + for (const audience of audiences.values()) { + schemaContent += schemaToTypeString(audience.request.describe()) + '\n\n'; } } }); }); + + fs.writeFileSync('schema.ts', schemaContent); + console.log(` Wrote schema to schema.ts`); +} + +function schemaToTypeString(schema: yup.SchemaFieldDescription, indent: number = 0): string { + switch (schema.type) { + case 'object': { + return '{\n' + Object.entries((schema as any).fields).map(([key, value]): any => `${' '.repeat(indent + 1)}${key}: ${schemaToTypeString(value as any, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + '}'; + } + case 'array': { + return `${schemaToTypeString((schema as any).innerType, indent + 1)}[]`; + } + case 'tuple': { + return '[\n' + (schema as any).innerType.map((value: any) => `${' '.repeat(indent + 1)}${schemaToTypeString(value, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + ']'; + } + case 'mixed': { + return 'any'; + } + case 'string': { + return 'string'; + } + case 'number': { + return 'number'; + } + case 'boolean': { + return 'boolean'; + } + default: { + throw new Error(`Unsupported schema type: ${schema.type}`); + } + } } main().catch((...args) => { From 861ad4e21653c68c09ff5b8485f7fdbb947248af Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 9 Dec 2024 16:31:23 -0800 Subject: [PATCH 05/40] type schema gen --- apps/backend/scripts/generate-schema.ts | 30 +++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 3d28ed45b..345531346 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -5,18 +5,12 @@ async function main() { console.log("Started docs schema generator"); const endpoints = await listEndpoints("api/v1"); - let schemaContent = ''; + let schemaContent = 'type EndpointSchema = {\n'; endpoints.forEach((handlersByMethod, url) => { - handlersByMethod.forEach((handler, method) => { - console.log(` ${method}`); - - if (handler.overloads.size === 1) { - console.log(' overloads: 1'); - } else { - console.log(` overloads: ${handler.overloads.size}`); - } + let endpointContent = ''; + handlersByMethod.forEach((handler, method) => { const audiences = new Map(); for (const overload of handler.overloads.values()) { for (const audience of ['client', 'server', 'admin'] as const) { @@ -33,15 +27,23 @@ async function main() { } if (audiences.size === 0) { - schemaContent += schemaToTypeString(handler.overloads.values().next().value.request.describe()); + endpointContent = '{\n default: ' + schemaToTypeString(handler.overloads.values().next().value.request.describe()) + '\n }'; } else { - for (const audience of audiences.values()) { - schemaContent += schemaToTypeString(audience.request.describe()) + '\n\n'; - } + endpointContent = '{\n' + Array.from(audiences.entries()).map(([audience, overload]) => { + return ` ${audience}: {\n` + + Object.entries(overload.request.describe().fields) + .map(([key, value]): any => ` ${key}: ${schemaToTypeString(value as any, 2)}`) + .join(',\n') + + '\n }'; + }).join(',\n') + '\n }'; } }); + + schemaContent += `"${url || '/'}": ${endpointContent},\n`; }); + schemaContent += '}'; + fs.writeFileSync('schema.ts', schemaContent); console.log(` Wrote schema to schema.ts`); } @@ -49,7 +51,7 @@ async function main() { function schemaToTypeString(schema: yup.SchemaFieldDescription, indent: number = 0): string { switch (schema.type) { case 'object': { - return '{\n' + Object.entries((schema as any).fields).map(([key, value]): any => `${' '.repeat(indent + 1)}${key}: ${schemaToTypeString(value as any, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + '}'; + return '{\n' + Object.entries((schema as any).fields).map(([key, value]): any => `${' '.repeat(indent + 1)}"${key}": ${schemaToTypeString(value as any, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + '}'; } case 'array': { return `${schemaToTypeString((schema as any).innerType, indent + 1)}[]`; From 5878bab3b63c002c6a839c192e5dd0f65fcaf49a Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 9 Dec 2024 17:41:35 -0800 Subject: [PATCH 06/40] used prettier for formatting --- apps/backend/package.json | 1 + apps/backend/scripts/generate-schema.ts | 42 ++++++++++++++++--------- pnpm-lock.yaml | 10 ++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 56b50e6aa..d522f1af9 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -62,6 +62,7 @@ "oslo": "^1.2.1", "pg": "^8.11.3", "posthog-node": "^4.1.0", + "prettier": "^3.4.2", "react": "^18.2", "react-dom": "^18.2", "semver": "^7.6.3", diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 345531346..33323f5a5 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -1,14 +1,17 @@ import { listEndpoints } from '@/lib/glob'; import fs from 'fs'; +import * as prettier from "prettier"; import * as yup from 'yup'; + + async function main() { console.log("Started docs schema generator"); const endpoints = await listEndpoints("api/v1"); - let schemaContent = 'type EndpointSchema = {\n'; + let schemaContent = 'type EndpointSchema = {'; endpoints.forEach((handlersByMethod, url) => { - let endpointContent = ''; + let methodContent = '{'; handlersByMethod.forEach((handler, method) => { const audiences = new Map(); @@ -26,38 +29,47 @@ async function main() { throw new Error(`Expected ${handler.overloads.size} audiences, got ${audiences.size}. Multiple audiences other than client, server, and admin is currently not supported. You might need to manually fix this.`); } + let endpointContent; if (audiences.size === 0) { - endpointContent = '{\n default: ' + schemaToTypeString(handler.overloads.values().next().value.request.describe()) + '\n }'; + endpointContent = '{default: ' + schemaToTypeString(handler.overloads.values().next().value.request.describe()) + '}'; } else { - endpointContent = '{\n' + Array.from(audiences.entries()).map(([audience, overload]) => { - return ` ${audience}: {\n` + + endpointContent = '{' + Array.from(audiences.entries()).map(([audience, overload]) => { + return `${audience}: {` + Object.entries(overload.request.describe().fields) - .map(([key, value]): any => ` ${key}: ${schemaToTypeString(value as any, 2)}`) - .join(',\n') + - '\n }'; - }).join(',\n') + '\n }'; + .map(([key, value]): any => `${key}: ${schemaToTypeString(value as any)}`) + .join(',') + + '}'; + }).join(',') + '}'; } + + methodContent += `${method}: ${endpointContent},`; }); - schemaContent += `"${url || '/'}": ${endpointContent},\n`; + methodContent += '}'; + schemaContent += `"${url || '/'}": ${methodContent},`; }); schemaContent += '}'; - fs.writeFileSync('schema.ts', schemaContent); + fs.writeFileSync('schema.ts', await prettier.format(schemaContent, { + parser: "typescript", + semi: true, + singleQuote: true, + trailingComma: "all", + })); console.log(` Wrote schema to schema.ts`); } -function schemaToTypeString(schema: yup.SchemaFieldDescription, indent: number = 0): string { +function schemaToTypeString(schema: yup.SchemaFieldDescription): string { switch (schema.type) { case 'object': { - return '{\n' + Object.entries((schema as any).fields).map(([key, value]): any => `${' '.repeat(indent + 1)}"${key}": ${schemaToTypeString(value as any, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + '}'; + return '{' + Object.entries((schema as any).fields).map(([key, value]): any => `"${key}": ${schemaToTypeString(value as any)}`).join(',') + '}'; } case 'array': { - return `${schemaToTypeString((schema as any).innerType, indent + 1)}[]`; + return `${schemaToTypeString((schema as any).innerType)}[]`; } case 'tuple': { - return '[\n' + (schema as any).innerType.map((value: any) => `${' '.repeat(indent + 1)}${schemaToTypeString(value, indent + 1)}`).join(',\n') + '\n' + ' '.repeat(indent) + ']'; + return '[' + (schema as any).innerType.map((value: any) => `${schemaToTypeString(value)}`).join(',') + ']'; } case 'mixed': { return 'any'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0791b135..926d964e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -179,6 +179,9 @@ importers: posthog-node: specifier: ^4.1.0 version: 4.1.0 + prettier: + specifier: ^3.4.2 + version: 3.4.2 react: specifier: ^18.2 version: 18.3.1 @@ -9276,6 +9279,11 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + engines: {node: '>=14'} + hasBin: true + pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -20129,6 +20137,8 @@ snapshots: prettier@2.8.8: {} + prettier@3.4.2: {} + pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 From e4739c66a58a809811cb2729957759122441ceae Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Tue, 10 Dec 2024 11:02:52 -0800 Subject: [PATCH 07/40] added import gen --- apps/backend/scripts/generate-docs.ts | 2 +- apps/backend/scripts/generate-schema.ts | 76 ++++++++++++------------- apps/backend/src/lib/glob.tsx | 6 +- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/apps/backend/scripts/generate-docs.ts b/apps/backend/scripts/generate-docs.ts index 4e62fce02..feeeca843 100644 --- a/apps/backend/scripts/generate-docs.ts +++ b/apps/backend/scripts/generate-docs.ts @@ -9,7 +9,7 @@ async function main() { for (const audience of ['client', 'server', 'admin'] as const) { const openAPISchema = yaml.stringify(parseOpenAPI({ - endpoints: await listEndpoints("api/v1"), + endpoints: await listEndpoints("api/v1", true, true), audience, })); fs.writeFileSync(`../../docs/fern/openapi/${audience}.yaml`, openAPISchema); diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 33323f5a5..d6d9bcf18 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -4,60 +4,58 @@ import * as prettier from "prettier"; import * as yup from 'yup'; +function convertUrlToJSVariable(url: string, method: string) { + return 'i' + url.replaceAll('[', '') + .replaceAll(']', '') + .replaceAll('.', '') + .replaceAll('/', '_') + .replaceAll('-', '_') + .replace(/_[a-z]/g, match => match[1].toUpperCase()) + .replace(/^[a-z]/, match => match.toUpperCase()) + + method.slice(0, 1).toUpperCase() + method.slice(1).toLowerCase(); +} + + async function main() { console.log("Started docs schema generator"); - const endpoints = await listEndpoints("api/v1"); - let schemaContent = 'type EndpointSchema = {'; + const endpoints = await listEndpoints("api/v1", false); - endpoints.forEach((handlersByMethod, url) => { - let methodContent = '{'; - - handlersByMethod.forEach((handler, method) => { - const audiences = new Map(); - for (const overload of handler.overloads.values()) { - for (const audience of ['client', 'server', 'admin'] as const) { - const schemaAudience = overload.request.describe().fields.auth?.fields?.type; - if (!schemaAudience) continue; - if ("oneOf" in schemaAudience && schemaAudience.oneOf.length > 0 && schemaAudience.oneOf.includes(audience)) { - audiences.set(audience, overload); - } - } - } + // ========== generate schema.ts ========== + let schemaContent = 'export type EndpointSchema = {'; - if (handler.overloads.size !== 1 && audiences.size !== handler.overloads.size) { - throw new Error(`Expected ${handler.overloads.size} audiences, got ${audiences.size}. Multiple audiences other than client, server, and admin is currently not supported. You might need to manually fix this.`); - } + endpoints.forEach((handlersByMethod, url) => { + schemaContent += `"${url || '/'}": {${Array.from(handlersByMethod.keys()).map(method => `${method}: true`).join(',')}},`; + }); - let endpointContent; - if (audiences.size === 0) { - endpointContent = '{default: ' + schemaToTypeString(handler.overloads.values().next().value.request.describe()) + '}'; - } else { - endpointContent = '{' + Array.from(audiences.entries()).map(([audience, overload]) => { - return `${audience}: {` + - Object.entries(overload.request.describe().fields) - .map(([key, value]): any => `${key}: ${schemaToTypeString(value as any)}`) - .join(',') + - '}'; - }).join(',') + '}'; - } + schemaContent += '}'; + // ======================================== - methodContent += `${method}: ${endpointContent},`; - }); + // ========== generate imports.ts ========== + let importHeaders = ''; + let importBody = 'export const endpoints = {'; - methodContent += '}'; - schemaContent += `"${url || '/'}": ${methodContent},`; + endpoints.forEach((handlersByMethod, url) => { + let methodBody = ''; + for (const method of handlersByMethod.keys()) { + importHeaders += `import { ${method} as ${convertUrlToJSVariable(url, method)} } from "../..${url}";\n`; + methodBody += `"${method}": ${convertUrlToJSVariable(url, method)},`; + } + importBody += `"${url || '/'}": {${methodBody}},\n`; }); - schemaContent += '}'; + importBody += '}'; + // ======================================== - fs.writeFileSync('schema.ts', await prettier.format(schemaContent, { + const prettierConfig = { parser: "typescript", semi: true, singleQuote: true, trailingComma: "all", - })); - console.log(` Wrote schema to schema.ts`); + } as const; + + fs.writeFileSync('schema.ts', await prettier.format(schemaContent, prettierConfig)); + fs.writeFileSync('imports.ts', await prettier.format(importHeaders + '\n' + importBody, prettierConfig)); } function schemaToTypeString(schema: yup.SchemaFieldDescription): string { diff --git a/apps/backend/src/lib/glob.tsx b/apps/backend/src/lib/glob.tsx index c3f103bee..e306d56cc 100644 --- a/apps/backend/src/lib/glob.tsx +++ b/apps/backend/src/lib/glob.tsx @@ -4,7 +4,7 @@ import { typedKeys } from '@stackframe/stack-shared/dist/utils/objects'; import { glob } from 'glob'; import path from 'path'; -export async function listEndpoints(basePath: string) { +export async function listEndpoints(basePath: string, onlySmartRouteHandlers: boolean, replaceBracketsWithCurlyBraces: boolean = false) { const filePathPrefix = path.resolve(process.platform === "win32" ? `apps/src/app/${basePath}` : `src/app/${basePath}`); const importPathPrefix = `@/app/${basePath}`; const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")]; @@ -15,12 +15,12 @@ export async function listEndpoints(basePath: string) { } const suffix = filePath.slice(filePathPrefix.length); const midfix = suffix.slice(0, suffix.lastIndexOf("/route.")); + const urlPath = replaceBracketsWithCurlyBraces ? midfix.replaceAll("[", "{").replaceAll("]", "}") : midfix; const importPath = `${importPathPrefix}${suffix}`; - const urlPath = midfix.replaceAll("[", "{").replaceAll("]", "}"); const myModule = require(importPath); const handlersByMethod = new Map( typedKeys(HTTP_METHODS).map(method => [method, myModule[method]] as const) - .filter(([_, handler]) => isSmartRouteHandler(handler)) + .filter(([_, handler]) => isSmartRouteHandler(handler) || (!onlySmartRouteHandlers && handler)) ); return [urlPath, handlersByMethod] as const; }))); From 83b99bcceb994a5662aaf5230b218d71a0f4fd77 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Tue, 10 Dec 2024 16:53:28 -0800 Subject: [PATCH 08/40] schema gen --- apps/backend/scripts/generate-schema.ts | 40 +--- pnpm-lock.yaml | 292 ++++++++++++++++++++---- 2 files changed, 255 insertions(+), 77 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index d6d9bcf18..4259e3132 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -1,8 +1,6 @@ import { listEndpoints } from '@/lib/glob'; import fs from 'fs'; -import * as prettier from "prettier"; -import * as yup from 'yup'; - +import prettier from 'prettier'; function convertUrlToJSVariable(url: string, method: string) { return 'i' + url.replaceAll('[', '') @@ -29,7 +27,6 @@ async function main() { }); schemaContent += '}'; - // ======================================== // ========== generate imports.ts ========== let importHeaders = ''; @@ -38,7 +35,7 @@ async function main() { endpoints.forEach((handlersByMethod, url) => { let methodBody = ''; for (const method of handlersByMethod.keys()) { - importHeaders += `import { ${method} as ${convertUrlToJSVariable(url, method)} } from "../..${url}";\n`; + importHeaders += `import { ${method} as ${convertUrlToJSVariable(url, method)} } from "../../api/v1${url}/route";\n`; methodBody += `"${method}": ${convertUrlToJSVariable(url, method)},`; } importBody += `"${url || '/'}": {${methodBody}},\n`; @@ -54,37 +51,8 @@ async function main() { trailingComma: "all", } as const; - fs.writeFileSync('schema.ts', await prettier.format(schemaContent, prettierConfig)); - fs.writeFileSync('imports.ts', await prettier.format(importHeaders + '\n' + importBody, prettierConfig)); -} - -function schemaToTypeString(schema: yup.SchemaFieldDescription): string { - switch (schema.type) { - case 'object': { - return '{' + Object.entries((schema as any).fields).map(([key, value]): any => `"${key}": ${schemaToTypeString(value as any)}`).join(',') + '}'; - } - case 'array': { - return `${schemaToTypeString((schema as any).innerType)}[]`; - } - case 'tuple': { - return '[' + (schema as any).innerType.map((value: any) => `${schemaToTypeString(value)}`).join(',') + ']'; - } - case 'mixed': { - return 'any'; - } - case 'string': { - return 'string'; - } - case 'number': { - return 'number'; - } - case 'boolean': { - return 'boolean'; - } - default: { - throw new Error(`Unsupported schema type: ${schema.type}`); - } - } + fs.writeFileSync('src/app/api/v2/schema.ts', await prettier.format(schemaContent, prettierConfig)); + fs.writeFileSync('src/app/api/v2/imports.ts', await prettier.format(importHeaders + '\n' + importBody, prettierConfig)); } main().catch((...args) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a2cdcb04..544c48aac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -926,7 +926,7 @@ importers: version: 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-email: specifier: 2.1.0 - version: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0) + version: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)) tailwind-merge: specifier: ^2.3.0 version: 2.3.0 @@ -2357,10 +2357,34 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.19.1': + resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.9.1': + resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@1.4.1': resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.2.0': + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.16.0': + resolution: {integrity: sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.5': + resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.4': + resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.6.2': resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==} @@ -2424,6 +2448,14 @@ packages: peerDependencies: react-hook-form: ^7.0.0 + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -2437,6 +2469,14 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.1': + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + '@img/sharp-darwin-arm64@0.33.4': resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -7019,6 +7059,10 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-utils@2.1.0: resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} engines: {node: '>=6'} @@ -7041,11 +7085,29 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@8.30.0: resolution: {integrity: sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true + eslint@9.16.0: + resolution: {integrity: sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7178,6 +7240,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -7201,6 +7267,10 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} @@ -7417,6 +7487,10 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -11299,7 +11373,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5 + debug: 4.3.7 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12057,8 +12131,25 @@ snapshots: eslint: 8.30.0 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.4.1(eslint@9.16.0(jiti@1.21.6))': + dependencies: + eslint: 9.16.0(jiti@1.21.6) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} + '@eslint/config-array@0.19.1': + dependencies: + '@eslint/object-schema': 2.1.5 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.9.1': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@1.4.1': dependencies: ajv: 6.12.6 @@ -12073,6 +12164,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/eslintrc@3.2.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.16.0': {} + + '@eslint/object-schema@2.1.5': {} + + '@eslint/plugin-kit@0.2.4': + dependencies: + levn: 0.4.1 + '@floating-ui/core@1.6.2': dependencies: '@floating-ui/utils': 0.2.2 @@ -12151,6 +12264,13 @@ snapshots: dependencies: react-hook-form: 7.53.1(react@18.3.1) + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -12163,6 +12283,10 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.1': {} + '@img/sharp-darwin-arm64@0.33.4': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.2 @@ -14341,7 +14465,7 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@react-email/components@0.0.15(@types/react@18.3.12)(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1)': + '@react-email/components@0.0.15(@types/react@18.3.12)(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1)': dependencies: '@react-email/body': 0.0.7(react@18.3.1) '@react-email/button': 0.0.14(react@18.3.1) @@ -14356,7 +14480,7 @@ snapshots: '@react-email/html': 0.0.7(react@18.3.1) '@react-email/img': 0.0.7(react@18.3.1) '@react-email/link': 0.0.7(react@18.3.1) - '@react-email/markdown': 0.0.8(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1) + '@react-email/markdown': 0.0.8(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1) '@react-email/preview': 0.0.8(react@18.3.1) '@react-email/render': 0.0.12 '@react-email/row': 0.0.7(react@18.3.1) @@ -14438,9 +14562,9 @@ snapshots: dependencies: react: 18.3.1 - '@react-email/markdown@0.0.8(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1)': + '@react-email/markdown@0.0.8(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1)': dependencies: - md-to-react-email: 4.1.0(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1) + md-to-react-email: 4.1.0(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1) react: 18.3.1 transitivePeerDependencies: - react-email @@ -14980,6 +15104,23 @@ snapshots: '@swc/core-win32-x64-msvc@1.3.101': optional: true + '@swc/core@1.3.101': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.8 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.101 + '@swc/core-darwin-x64': 1.3.101 + '@swc/core-linux-arm-gnueabihf': 1.3.101 + '@swc/core-linux-arm64-gnu': 1.3.101 + '@swc/core-linux-arm64-musl': 1.3.101 + '@swc/core-linux-x64-gnu': 1.3.101 + '@swc/core-linux-x64-musl': 1.3.101 + '@swc/core-win32-arm64-msvc': 1.3.101 + '@swc/core-win32-ia32-msvc': 1.3.101 + '@swc/core-win32-x64-msvc': 1.3.101 + optional: true + '@swc/core@1.3.101(@swc/helpers@0.5.13)': dependencies: '@swc/counter': 0.1.3 @@ -15070,7 +15211,7 @@ snapshots: '@types/acorn@4.0.6': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/aria-query@5.0.4': {} @@ -15158,16 +15299,16 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 8.56.10 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/eslint@8.56.10': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/estree@1.0.5': {} @@ -15622,6 +15763,10 @@ snapshots: dependencies: acorn: 8.12.0 + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn-walk@8.3.4: dependencies: acorn: 8.14.0 @@ -17040,9 +17185,9 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-config-prettier@9.0.0(eslint@8.30.0): + eslint-config-prettier@9.0.0(eslint@9.16.0(jiti@1.21.6)): dependencies: - eslint: 8.30.0 + eslint: 9.16.0(jiti@1.21.6) eslint-config-standard-with-typescript@43.0.1(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.30.0)(typescript@5.3.3))(eslint@8.30.0)(typescript@5.3.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.30.0)(typescript@5.3.3))(eslint@8.30.0))(eslint-plugin-n@16.6.2(eslint@8.30.0))(eslint-plugin-promise@6.6.0(eslint@8.30.0))(eslint@8.30.0)(typescript@5.3.3): dependencies: @@ -17064,10 +17209,10 @@ snapshots: eslint-plugin-n: 16.6.2(eslint@8.30.0) eslint-plugin-promise: 6.6.0(eslint@8.30.0) - eslint-config-turbo@1.10.12(eslint@8.30.0): + eslint-config-turbo@1.10.12(eslint@9.16.0(jiti@1.21.6)): dependencies: - eslint: 8.30.0 - eslint-plugin-turbo: 1.10.12(eslint@8.30.0) + eslint: 9.16.0(jiti@1.21.6) + eslint-plugin-turbo: 1.10.12(eslint@9.16.0(jiti@1.21.6)) eslint-import-resolver-node@0.3.9: dependencies: @@ -17304,10 +17449,10 @@ snapshots: string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@1.10.12(eslint@8.30.0): + eslint-plugin-turbo@1.10.12(eslint@9.16.0(jiti@1.21.6)): dependencies: dotenv: 16.0.3 - eslint: 8.30.0 + eslint: 9.16.0(jiti@1.21.6) eslint-scope@5.1.1: dependencies: @@ -17319,6 +17464,11 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-utils@2.1.0: dependencies: eslint-visitor-keys: 1.3.0 @@ -17334,6 +17484,8 @@ snapshots: eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.0: {} + eslint@8.30.0: dependencies: '@eslint/eslintrc': 1.4.1 @@ -17378,6 +17530,53 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.16.0(jiti@1.21.6): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.16.0(jiti@1.21.6)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.1 + '@eslint/core': 0.9.1 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.16.0 + '@eslint/plugin-kit': 0.2.4 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.5 + debug: 4.3.7 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.6 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + espree@9.6.1: dependencies: acorn: 8.12.0 @@ -17400,7 +17599,7 @@ snapshots: estree-util-attach-comments@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-util-build-jsx@3.0.1: dependencies: @@ -17426,7 +17625,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 esutils@2.0.3: {} @@ -17510,6 +17709,10 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -17536,6 +17739,11 @@ snapshots: keyv: 4.5.4 rimraf: 3.0.2 + flat-cache@4.0.1: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + flatted@3.3.1: {} follow-redirects@1.15.6: {} @@ -17785,6 +17993,8 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@14.0.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -17918,7 +18128,7 @@ snapshots: hast-util-to-estree@3.1.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 @@ -17939,7 +18149,7 @@ snapshots: hast-util-to-jsx-runtime@2.3.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/hast': 3.0.4 '@types/unist': 3.0.2 comma-separated-tokens: 2.0.3 @@ -18339,7 +18549,7 @@ snapshots: is-reference@3.0.2: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 is-regex@1.1.4: dependencies: @@ -18714,11 +18924,11 @@ snapshots: '@types/minimatch': 3.0.5 minimatch: 3.1.2 - md-to-react-email@4.1.0(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1): + md-to-react-email@4.1.0(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1): dependencies: marked: 7.0.4 react: 18.3.1 - react-email: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0) + react-email: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)) mdast-util-find-and-replace@3.0.1: dependencies: @@ -19016,7 +19226,7 @@ snapshots: micromark-extension-mdx-expression@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 micromark-factory-mdx-expression: 2.0.1 micromark-factory-space: 2.0.0 @@ -19028,7 +19238,7 @@ snapshots: micromark-extension-mdx-jsx@3.0.0: dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 micromark-factory-mdx-expression: 2.0.1 @@ -19044,7 +19254,7 @@ snapshots: micromark-extension-mdxjs-esm@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 micromark-util-character: 2.1.0 @@ -19080,7 +19290,7 @@ snapshots: micromark-factory-mdx-expression@2.0.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 micromark-util-character: 2.1.0 micromark-util-events-to-acorn: 2.0.2 @@ -19144,7 +19354,7 @@ snapshots: micromark-util-events-to-acorn@2.0.2: dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/unist': 3.0.2 devlop: 1.1.0 estree-util-visit: 2.0.0 @@ -19877,7 +20087,7 @@ snapshots: periscopic@3.1.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 3.0.3 is-reference: 3.0.2 @@ -20257,7 +20467,7 @@ snapshots: react: 19.0.0-rc-65a56d0e-20241020 scheduler: 0.25.0-rc-65a56d0e-20241020 - react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0): + react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)): dependencies: '@radix-ui/colors': 1.0.1 '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -20265,7 +20475,7 @@ snapshots: '@radix-ui/react-slot': 1.0.2(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': 1.0.6(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-email/components': 0.0.15(@types/react@18.3.12)(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0))(react@18.3.1) + '@react-email/components': 0.0.15(@types/react@18.3.12)(react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)))(react@18.3.1) '@react-email/render': 0.0.12 '@swc/core': 1.3.101(@swc/helpers@0.5.13) '@types/react': 18.3.12 @@ -20278,8 +20488,8 @@ snapshots: commander: 11.1.0 debounce: 2.0.0 esbuild: 0.19.11 - eslint-config-prettier: 9.0.0(eslint@8.30.0) - eslint-config-turbo: 1.10.12(eslint@8.30.0) + eslint-config-prettier: 9.0.0(eslint@9.16.0(jiti@1.21.6)) + eslint-config-turbo: 1.10.12(eslint@9.16.0(jiti@1.21.6)) framer-motion: 10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) glob: 10.3.4 log-symbols: 4.1.0 @@ -21415,7 +21625,7 @@ snapshots: terser: 5.31.1 webpack: 5.92.0(@swc/core@1.3.101)(esbuild@0.24.0) optionalDependencies: - '@swc/core': 1.3.101(@swc/helpers@0.5.13) + '@swc/core': 1.3.101 esbuild: 0.24.0 terser@5.31.1: @@ -21575,7 +21785,7 @@ snapshots: tinyglobby: 0.2.10 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.3.101(@swc/helpers@0.5.13) + '@swc/core': 1.3.101 postcss: 8.4.47 typescript: 5.3.3 transitivePeerDependencies: @@ -22091,7 +22301,7 @@ snapshots: webpack@5.92.0(@swc/core@1.3.101(@swc/helpers@0.5.13))(esbuild@0.19.11): dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 @@ -22122,15 +22332,15 @@ snapshots: webpack@5.92.0(@swc/core@1.3.101)(esbuild@0.24.0): dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.0 - acorn-import-attributes: 1.9.5(acorn@8.12.0) + acorn: 8.14.0 + acorn-import-attributes: 1.9.5(acorn@8.14.0) browserslist: 4.24.2 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.0 + enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 eslint-scope: 5.1.1 events: 3.3.0 From 84daece290e668499cf03a0aa58b90a010afc606 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Tue, 10 Dec 2024 19:32:49 -0800 Subject: [PATCH 09/40] added url match --- .../backend/src/app/api/v2/[...all]/route.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 apps/backend/src/app/api/v2/[...all]/route.tsx diff --git a/apps/backend/src/app/api/v2/[...all]/route.tsx b/apps/backend/src/app/api/v2/[...all]/route.tsx new file mode 100644 index 000000000..e04343d72 --- /dev/null +++ b/apps/backend/src/app/api/v2/[...all]/route.tsx @@ -0,0 +1,49 @@ + +import { NextRequest, NextResponse } from 'next/server'; + +function urlMatch(url: string, nextPattern: string): Record | null { + const keys: string[] = []; + + const regexPattern = nextPattern + // match (name) + .replace(/\/\(.*?\)/g, '') + // match [...] + .replace(/\[\.\.\.\]/g, '.*') + // match [...name] + .replace(/\[\.\.\.\w+\]/g, (match) => { + const key = match.slice(1, -1); + keys.push(key); + return '(.*?)'; + }) + // match [name] + .replace(/\[\w+\]/g, (match) => { + const key = match.slice(1, -1); + keys.push(key); + return '([^/]+)'; + }); + + const regex = new RegExp(`^${regexPattern}$`); + const match = regex.exec(url); + + if (!match) return null; + + const result: Record = {}; + keys.forEach((key, index) => { + const value = match[index + 1]; + if (key.startsWith('...')) { + result[key.slice(3)] = value.split('/'); + } else { + result[key] = value; + } + }); + + return result; +} + +export const GET = (req: NextRequest) => { + const match = urlMatch(new URL(req.url).pathname, '/api/v2/(example)/[id]/[...]'); + + return NextResponse.json(match); +}; + + From 194b4ea3f10eefbfad2e480f565aba52013730b7 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Tue, 10 Dec 2024 21:23:57 -0800 Subject: [PATCH 10/40] created migration route --- .../backend/src/app/api/v2/[...all]/route.tsx | 65 +++++-------------- .../src/route-handlers/migration-route.tsx | 64 ++++++++++++++++++ 2 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 apps/backend/src/route-handlers/migration-route.tsx diff --git a/apps/backend/src/app/api/v2/[...all]/route.tsx b/apps/backend/src/app/api/v2/[...all]/route.tsx index e04343d72..0802f0bc5 100644 --- a/apps/backend/src/app/api/v2/[...all]/route.tsx +++ b/apps/backend/src/app/api/v2/[...all]/route.tsx @@ -1,49 +1,16 @@ - -import { NextRequest, NextResponse } from 'next/server'; - -function urlMatch(url: string, nextPattern: string): Record | null { - const keys: string[] = []; - - const regexPattern = nextPattern - // match (name) - .replace(/\/\(.*?\)/g, '') - // match [...] - .replace(/\[\.\.\.\]/g, '.*') - // match [...name] - .replace(/\[\.\.\.\w+\]/g, (match) => { - const key = match.slice(1, -1); - keys.push(key); - return '(.*?)'; - }) - // match [name] - .replace(/\[\w+\]/g, (match) => { - const key = match.slice(1, -1); - keys.push(key); - return '([^/]+)'; - }); - - const regex = new RegExp(`^${regexPattern}$`); - const match = regex.exec(url); - - if (!match) return null; - - const result: Record = {}; - keys.forEach((key, index) => { - const value = match[index + 1]; - if (key.startsWith('...')) { - result[key.slice(3)] = value.split('/'); - } else { - result[key] = value; - } - }); - - return result; -} - -export const GET = (req: NextRequest) => { - const match = urlMatch(new URL(req.url).pathname, '/api/v2/(example)/[id]/[...]'); - - return NextResponse.json(match); -}; - - +import { NextResponse } from 'next/server'; +import { createMigrationRoute } from '../../../../route-handlers/migration-route'; + +const route = createMigrationRoute({ + '/api/v1/users': { + GET: async (req) => { + return NextResponse.json({ 'hi': 'asdf' }); + }, + }, +}); + +export const GET = route.GET; +export const POST = route.POST; +export const PUT = route.PUT; +export const DELETE = route.DELETE; +export const PATCH = route.PATCH; diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx new file mode 100644 index 000000000..67aba32c8 --- /dev/null +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -0,0 +1,64 @@ +import { typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; +import { HTTP_METHODS } from "next/dist/server/web/http"; +import { NextRequest, NextResponse } from "next/server"; + +type Endpoints = { + [key: string]: { + [key in (typeof HTTP_METHODS)[number]]?: (req: NextRequest) => Promise + }, +}; + +function urlMatch(url: string, nextPattern: string): Record | null { + const keys: string[] = []; + + const regexPattern = nextPattern + // match (name) + .replace(/\/\(.*?\)/g, '') + // match [...] + .replace(/\[\.\.\.\]/g, '.*') + // match [...name] + .replace(/\[\.\.\.\w+\]/g, (match) => { + const key = match.slice(1, -1); + keys.push(key); + return '(.*?)'; + }) + // match [name] + .replace(/\[\w+\]/g, (match) => { + const key = match.slice(1, -1); + keys.push(key); + return '([^/]+)'; + }); + + const regex = new RegExp(`^${regexPattern}$`); + const match = regex.exec(url); + + if (!match) return null; + + const result: Record = {}; + keys.forEach((key, index) => { + const value = match[index + 1]; + if (key.startsWith('...')) { + result[key.slice(3)] = value.split('/'); + } else { + result[key] = value; + } + }); + + return result; +} + +export function createMigrationRoute(newEndpoints: Endpoints) { + return typedFromEntries(HTTP_METHODS.map((method) => { + return [method, (req: NextRequest) => { + for (const url of Object.keys(newEndpoints)) { + const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); + if (!match) { + return NextResponse.json({ error: 'Not Found' }, { status: 404 }); + } else { + return NextResponse.json({ match, url: req.url, nextUrl: url }); + } + } + }]; + })); +} + From 932a025360e1ffd9555480c4574fe0f1757673d6 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 11:12:08 -0800 Subject: [PATCH 11/40] updated create migration route --- apps/backend/src/app/api/v2/[...all]/route.tsx | 2 +- apps/backend/src/route-handlers/migration-route.tsx | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/app/api/v2/[...all]/route.tsx b/apps/backend/src/app/api/v2/[...all]/route.tsx index 0802f0bc5..c127b5241 100644 --- a/apps/backend/src/app/api/v2/[...all]/route.tsx +++ b/apps/backend/src/app/api/v2/[...all]/route.tsx @@ -3,7 +3,7 @@ import { createMigrationRoute } from '../../../../route-handlers/migration-route const route = createMigrationRoute({ '/api/v1/users': { - GET: async (req) => { + POST: async (req) => { return NextResponse.json({ 'hi': 'asdf' }); }, }, diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 67aba32c8..52dc08d61 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -50,12 +50,16 @@ function urlMatch(url: string, nextPattern: string): Record | null export function createMigrationRoute(newEndpoints: Endpoints) { return typedFromEntries(HTTP_METHODS.map((method) => { return [method, (req: NextRequest) => { - for (const url of Object.keys(newEndpoints)) { + for (const [url, endpointMethods] of Object.entries(newEndpoints)) { const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); if (!match) { - return NextResponse.json({ error: 'Not Found' }, { status: 404 }); + return new NextResponse(null, { status: 404 }); } else { - return NextResponse.json({ match, url: req.url, nextUrl: url }); + if (endpointMethods[method]) { + return NextResponse.json({ match, url: req.url, nextUrl: url }); + } else { + return new NextResponse(null, { status: 405 }); + } } } }]; From 99a0198819f78fcee85327b2c493284805b4ea84 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 16:19:21 -0800 Subject: [PATCH 12/40] added more parsing code --- .../src/route-handlers/migration-route.tsx | 30 +++++++++++++++++-- .../src/route-handlers/smart-request.tsx | 2 +- .../src/route-handlers/smart-response.tsx | 4 +-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 52dc08d61..a8e554b41 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -1,10 +1,14 @@ +import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto"; import { typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; -import { HTTP_METHODS } from "next/dist/server/web/http"; import { NextRequest, NextResponse } from "next/server"; +import { SmartRequest } from "./smart-request"; +import { SmartResponse, createResponse as createResponseFromSmartResponse } from "./smart-response"; + +const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; type Endpoints = { [key: string]: { - [key in (typeof HTTP_METHODS)[number]]?: (req: NextRequest) => Promise + [key in (typeof allowedMethods)[number]]?: (req: NextRequest) => Promise }, }; @@ -47,8 +51,28 @@ function urlMatch(url: string, nextPattern: string): Record | null return result; } +type ParsedRequest = Pick; +type ParsedResponse = SmartResponse; + +// async function convertRawToParsedRequest(req: NextRequest, options?: { params: Promise> }): Promise { +// const bodyBuffer = await req.arrayBuffer(); +// const smartRequest = await createSmartRequest(req, bodyBuffer, options); +// return pick(smartRequest, ["url", "method", "body", "headers", "query", "params"]); +// } + +// async function convertParsedRequestToRaw(req: ParsedRequest, ): Promise { + +// } + +async function createResponseFromParsedRequest(req: NextRequest, response: SmartResponse): Promise { + const requestId = generateSecureRandomString(80); + return await createResponseFromSmartResponse(req, requestId, response); +} + +// async function createSmartResponseFromResponse + export function createMigrationRoute(newEndpoints: Endpoints) { - return typedFromEntries(HTTP_METHODS.map((method) => { + return typedFromEntries(allowedMethods.map((method) => { return [method, (req: NextRequest) => { for (const [url, endpointMethods] of Object.entries(newEndpoints)) { const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); diff --git a/apps/backend/src/route-handlers/smart-request.tsx b/apps/backend/src/route-handlers/smart-request.tsx index f4fdce427..6f1271ffb 100644 --- a/apps/backend/src/route-handlers/smart-request.tsx +++ b/apps/backend/src/route-handlers/smart-request.tsx @@ -92,7 +92,7 @@ async function validate(obj: SmartRequest, schema: yup.Schema, req: NextRe } -async function parseBody(req: NextRequest, bodyBuffer: ArrayBuffer): Promise { +export async function parseBody(req: NextRequest, bodyBuffer: ArrayBuffer): Promise { const contentType = req.headers.get("content-type")?.split(";")[0]; const getText = () => { diff --git a/apps/backend/src/route-handlers/smart-response.tsx b/apps/backend/src/route-handlers/smart-response.tsx index 26d6568b9..9d23aae2b 100644 --- a/apps/backend/src/route-handlers/smart-response.tsx +++ b/apps/backend/src/route-handlers/smart-response.tsx @@ -57,8 +57,8 @@ function isBinaryBody(body: unknown): body is BodyInit { || ArrayBuffer.isView(body); } -export async function createResponse(req: NextRequest | null, requestId: string, obj: T, schema: yup.Schema): Promise { - const validated = await validate(req, obj, schema); +export async function createResponse(req: NextRequest | null, requestId: string, obj: T, schema?: yup.Schema): Promise { + const validated = schema ? await validate(req, obj, schema) : obj; let status = validated.statusCode; const headers = new Map(); From 596aa53229b6b6481679f4c9547c18eecca8c066 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 17:18:20 -0800 Subject: [PATCH 13/40] added yup type gen --- apps/backend/scripts/generate-schema.ts | 140 +++++++++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 4259e3132..ab22dd3b7 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -1,6 +1,8 @@ import { listEndpoints } from '@/lib/glob'; +import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler'; import fs from 'fs'; import prettier from 'prettier'; +import * as yup from 'yup'; function convertUrlToJSVariable(url: string, method: string) { return 'i' + url.replaceAll('[', '') @@ -20,10 +22,52 @@ async function main() { const endpoints = await listEndpoints("api/v1", false); // ========== generate schema.ts ========== - let schemaContent = 'export type EndpointSchema = {'; + let schemaContent = '/* eslint-disable no-restricted-syntax */\n'; + schemaContent += 'import { yupObject, yupArray, yupTuple, yupString, yupNumber, yupBoolean, yupMixed } from "@stackframe/stack-shared/dist/schema-fields";\n\n'; + schemaContent += 'const endpointSchema = {'; endpoints.forEach((handlersByMethod, url) => { - schemaContent += `"${url || '/'}": {${Array.from(handlersByMethod.keys()).map(method => `${method}: true`).join(',')}},`; + let methodContent = '{'; + + handlersByMethod.forEach((handler, method) => { + if (!isSmartRouteHandler(handler)) { + // TODO: handle non-smart route handlers + console.warn(`Skipping non-smart route handler: ${url} ${method}`); + return; + } + + const audiences = new Map(); + for (const overload of handler.overloads.values()) { + for (const audience of ['client', 'server', 'admin'] as const) { + const schemaAudience = (overload.request.describe() as any).fields.auth?.fields?.type; + if (!schemaAudience) continue; + if ("oneOf" in schemaAudience && schemaAudience.oneOf.length > 0 && schemaAudience.oneOf.includes(audience)) { + audiences.set(audience, overload); + } + } + } + + if (handler.overloads.size !== 1 && audiences.size !== handler.overloads.size) { + throw new Error(`Expected ${handler.overloads.size} audiences, got ${audiences.size}. Multiple audiences other than client, server, and admin is currently not supported. You might need to manually fix this.`); + } + + let endpointContent; + if (audiences.size === 0) { + endpointContent = '{default: ' + endpointSchemaToTypeString( + handler.overloads.values().next().value.request.describe(), + handler.overloads.values().next().value.response.describe() + ) + '}'; + } else { + endpointContent = '{' + Array.from(audiences.entries()).map(([audience, overload]) => { + return `${audience}: ${endpointSchemaToTypeString(overload.request.describe(), overload.response.describe())}`; + }).join(',') + '}'; + } + + methodContent += `${method}: ${endpointContent},`; + }); + + methodContent += '}'; + schemaContent += `"${url || '/'}": ${methodContent},`; }); schemaContent += '}'; @@ -55,6 +99,98 @@ async function main() { fs.writeFileSync('src/app/api/v2/imports.ts', await prettier.format(importHeaders + '\n' + importBody, prettierConfig)); } +function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSchema: yup.SchemaFieldDescription): string { + if (reqSchema.type !== 'object') { + throw new Error(`Unsupported schema type: ${reqSchema.type}`); + } + + const fields = Object.entries((reqSchema as any).fields); + + const newFields: Record = {}; + for (const key of ['body', 'query', 'body']) { + const field = fields.find(([k]) => k === key); + if (field) { + newFields[key] = field[1]; + } + } + + // newFields['response'] = resSchema; + + const copiedSchema = { ...reqSchema }; + (copiedSchema as any).fields = newFields; + return schemaToTypeString(copiedSchema); +} + +function schemaToTypeString(schema: yup.SchemaFieldDescription): string { + let result; + switch (schema.type) { + case 'object': { + result = `yupObject({${Object.entries((schema as any).fields).map(([key, value]): any => `"${key}": ${schemaToTypeString(value as any)}`).join(',')}})`; + break; + } + case 'array': { + result = `yupArray(${schemaToTypeString((schema as any).innerType)})`; + break; + } + case 'tuple': { + result = `yupTuple([${(schema as any).innerType.map((value: any) => schemaToTypeString(value)).join(',')}])`; + break; + } + case 'mixed': { + result = 'yupMixed()'; + break; + } + case 'string': { + result = 'yupString()'; + break; + } + case 'number': { + result = 'yupNumber()'; + break; + } + case 'boolean': { + result = 'yupBoolean()'; + break; + } + default: { + throw new Error(`Unsupported schema type: ${schema.type}`); + } + } + + const optional = (schema as any).optional; + const nullable = (schema as any).nullable; + + if (!optional && !nullable) { + result += '.defined()'; + } + + if (optional) { + result += '.optional()'; + } + + if (nullable) { + result += '.nullable()'; + } + + if ((schema as any).oneOf && (schema as any).oneOf.length > 0 && (schema as any).oneOf.every((value: any) => value !== undefined)) { + result += `.oneOf([${(schema as any).oneOf + .map((value: any) => { + if (typeof value === 'string') { + return `"${value}"`; + } else if (typeof value === 'boolean') { + return value ? 'true' : 'false'; + } else if (typeof value === 'number') { + return value.toString(); + } else { + throw new Error(`Unsupported oneOf value: ${value}`); + } + }) + .join(',')}])`; + } + + return result; +} + main().catch((...args) => { console.error(`ERROR! Could not generate schema`, ...args); process.exit(1); From 8fc63baab760f4f03070045d407388cfcbd9cdc8 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 17:18:56 -0800 Subject: [PATCH 14/40] added response type --- apps/backend/scripts/generate-schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index ab22dd3b7..a6b8056c6 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -114,7 +114,7 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc } } - // newFields['response'] = resSchema; + newFields['response'] = resSchema; const copiedSchema = { ...reqSchema }; (copiedSchema as any).fields = newFields; From 1d6f3673b2dea00d977db6d0b437f5300ea9774a Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 17:50:47 -0800 Subject: [PATCH 15/40] added more convertion files --- .../src/route-handlers/migration-route.tsx | 42 +++++++++++++------ .../src/route-handlers/smart-response.tsx | 4 +- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index a8e554b41..592f8dcaf 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -1,8 +1,11 @@ +import { yupValidate } from "@stackframe/stack-shared/dist/schema-fields"; import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto"; -import { typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; +import { pick, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; import { NextRequest, NextResponse } from "next/server"; -import { SmartRequest } from "./smart-request"; -import { SmartResponse, createResponse as createResponseFromSmartResponse } from "./smart-response"; +import * as yup from "yup"; +import { SmartRequest, createSmartRequest } from "./smart-request"; +import { SmartResponse, createResponse } from "./smart-response"; + const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; @@ -54,19 +57,34 @@ function urlMatch(url: string, nextPattern: string): Record | null type ParsedRequest = Pick; type ParsedResponse = SmartResponse; -// async function convertRawToParsedRequest(req: NextRequest, options?: { params: Promise> }): Promise { -// const bodyBuffer = await req.arrayBuffer(); -// const smartRequest = await createSmartRequest(req, bodyBuffer, options); -// return pick(smartRequest, ["url", "method", "body", "headers", "query", "params"]); -// } +async function convertRawToParsedRequest(req: NextRequest, schema: yup.Schema, options?: { params: Promise> }): Promise { + const bodyBuffer = await req.arrayBuffer(); + const smartRequest = await createSmartRequest(req, bodyBuffer, options); + const parsedRequest = pick(smartRequest, ["url", "method", "body", "headers", "query", "params"]); + return await yupValidate(schema, parsedRequest); +} -// async function convertParsedRequestToRaw(req: ParsedRequest, ): Promise { +async function convertParsedRequestToRaw(req: ParsedRequest): Promise { + const url = new URL(req.url); -// } + for (const [key, value] of Object.entries(req.query)) { + if (value !== undefined) { + url.searchParams.set(key, value); + } + } + + return new NextRequest(url.toString(), { + body: JSON.stringify(req.body), + method: req.method, + headers: typedFromEntries(Object.entries(req.headers) + .map(([key, value]): [string, string | undefined] => [key, (value?.constructor === Array ? value.join(',') : value) as string | undefined]) + .filter(([_, value]) => value !== undefined)) as HeadersInit, + }); +} -async function createResponseFromParsedRequest(req: NextRequest, response: SmartResponse): Promise { +async function convertParsedResponseToRaw(req: NextRequest, response: ParsedResponse, schema: yup.Schema): Promise { const requestId = generateSecureRandomString(80); - return await createResponseFromSmartResponse(req, requestId, response); + return await createResponse(req, requestId, response, schema); } // async function createSmartResponseFromResponse diff --git a/apps/backend/src/route-handlers/smart-response.tsx b/apps/backend/src/route-handlers/smart-response.tsx index 9d23aae2b..26d6568b9 100644 --- a/apps/backend/src/route-handlers/smart-response.tsx +++ b/apps/backend/src/route-handlers/smart-response.tsx @@ -57,8 +57,8 @@ function isBinaryBody(body: unknown): body is BodyInit { || ArrayBuffer.isView(body); } -export async function createResponse(req: NextRequest | null, requestId: string, obj: T, schema?: yup.Schema): Promise { - const validated = schema ? await validate(req, obj, schema) : obj; +export async function createResponse(req: NextRequest | null, requestId: string, obj: T, schema: yup.Schema): Promise { + const validated = await validate(req, obj, schema); let status = validated.statusCode; const headers = new Map(); From fe29802b047b6ad1633443c5fa0544976b58c65b Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 11 Dec 2024 18:19:31 -0800 Subject: [PATCH 16/40] implemented convertion --- .../src/route-handlers/migration-route.tsx | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 592f8dcaf..0a9897da8 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -87,7 +87,54 @@ async function convertParsedResponseToRaw(req: NextRequest, response: ParsedResp return await createResponse(req, requestId, response, schema); } -// async function createSmartResponseFromResponse +async function convertRawToParsedResponse(res: NextResponse, schema: yup.Schema): Promise { + // TODO validate schema + let contentType = res.headers.get("content-type"); + if (contentType) { + contentType = contentType.split(";")[0]; + } + + switch (contentType) { + case "application/json": { + return { + statusCode: res.status, + body: await res.json(), + bodyType: "json", + }; + } + case "text/plain": { + return { + statusCode: res.status, + body: await res.text(), + bodyType: "text", + }; + } + case "binary": { + return { + statusCode: res.status, + body: await res.arrayBuffer(), + bodyType: "binary", + }; + } + case "success": { + return { + statusCode: res.status, + body: undefined, + bodyType: "success", + }; + } + case undefined: { + return { + statusCode: res.status, + body: undefined, + bodyType: "empty", + }; + } + default: { + throw new Error(`Unsupported content type: ${contentType}`); + } + } +} export function createMigrationRoute(newEndpoints: Endpoints) { return typedFromEntries(allowedMethods.map((method) => { From 62e25cc5bac0bc3d54dc6c399a1dfefc84b61588 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 11:07:56 -0800 Subject: [PATCH 17/40] added more schemas --- .../backend/src/app/api/v2/[...all]/route.tsx | 2 +- .../src/route-handlers/migration-route.tsx | 102 +++++++++++++++--- 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/apps/backend/src/app/api/v2/[...all]/route.tsx b/apps/backend/src/app/api/v2/[...all]/route.tsx index c127b5241..0802f0bc5 100644 --- a/apps/backend/src/app/api/v2/[...all]/route.tsx +++ b/apps/backend/src/app/api/v2/[...all]/route.tsx @@ -3,7 +3,7 @@ import { createMigrationRoute } from '../../../../route-handlers/migration-route const route = createMigrationRoute({ '/api/v1/users': { - POST: async (req) => { + GET: async (req) => { return NextResponse.json({ 'hi': 'asdf' }); }, }, diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 0a9897da8..feb7e2f9a 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -3,15 +3,40 @@ import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/ import { pick, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; import { NextRequest, NextResponse } from "next/server"; import * as yup from "yup"; -import { SmartRequest, createSmartRequest } from "./smart-request"; -import { SmartResponse, createResponse } from "./smart-response"; +import { createSmartRequest } from "./smart-request"; +import { createResponse } from "./smart-response"; const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; -type Endpoints = { - [key: string]: { - [key in (typeof allowedMethods)[number]]?: (req: NextRequest) => Promise +type EndpointInputSchema = { + query: yup.Schema, + body: yup.Schema, +}; + +type EndpointOutputSchema = { + statusCode: yup.Schema, + headers: yup.Schema, + body: yup.Schema, +}; + +type EndpointsSchema = { + [url: string]: { + [method in (typeof allowedMethods)[number]]?: { + [overload: string]: { + query: yup.Schema, + body: yup.Schema, + response: yup.Schema, + }, + } + }, +}; + +type EndpointHandlers = { + [url: string]: { + [method in (typeof allowedMethods)[number]]?: { + [overload: string]: (req: ParsedRequest, options?: { params: Promise> }) => Promise>, + } }, }; @@ -54,17 +79,62 @@ function urlMatch(url: string, nextPattern: string): Record | null return result; } -type ParsedRequest = Pick; -type ParsedResponse = SmartResponse; - -async function convertRawToParsedRequest(req: NextRequest, schema: yup.Schema, options?: { params: Promise> }): Promise { +type ParsedRequest> = { + url: string, + method: typeof allowedMethods[number], + headers: Record, + body: Body, + query: Query, +}; +type ParsedResponse = { + statusCode: number, + headers?: Record, +} & ( + | { + bodyType?: undefined, + body?: Body, + } + | { + bodyType: "empty", + body?: undefined, + } + | { + bodyType: "text", + body: string, + } + | { + bodyType: "json", + body: Body, + } + | { + bodyType: "binary", + body: ArrayBuffer, + } + | { + bodyType: "success", + body?: undefined, + } +) + +async function convertRawToParsedRequest( + req: NextRequest, + schema: EndpointInputSchema, + options?: { params: Promise> } +): Promise, + yup.InferType +>> { const bodyBuffer = await req.arrayBuffer(); const smartRequest = await createSmartRequest(req, bodyBuffer, options); - const parsedRequest = pick(smartRequest, ["url", "method", "body", "headers", "query", "params"]); - return await yupValidate(schema, parsedRequest); + const parsedRequest = pick(smartRequest, ["url", "method", "body", "headers", "query"]); + return { + ...parsedRequest, + body: await yupValidate(schema.body, parsedRequest.body), + query: await yupValidate(schema.query, parsedRequest.query), + }; } -async function convertParsedRequestToRaw(req: ParsedRequest): Promise { +async function convertParsedRequestToRaw(req: ParsedRequest>): Promise { const url = new URL(req.url); for (const [key, value] of Object.entries(req.query)) { @@ -82,12 +152,12 @@ async function convertParsedRequestToRaw(req: ParsedRequest): Promise { +async function convertParsedResponseToRaw(req: NextRequest, response: ParsedResponse, schema: yup.Schema): Promise { const requestId = generateSecureRandomString(80); return await createResponse(req, requestId, response, schema); } -async function convertRawToParsedResponse(res: NextResponse, schema: yup.Schema): Promise { +async function convertRawToParsedResponse(res: NextResponse, schema: EndpointOutputSchema): Promise>> { // TODO validate schema let contentType = res.headers.get("content-type"); if (contentType) { @@ -136,9 +206,9 @@ async function convertRawToParsedResponse(res: NextResponse, schema: yup.Schema) } } -export function createMigrationRoute(newEndpoints: Endpoints) { +export function createMigrationRoute(versionsSchema: EndpointsSchema[]) { return typedFromEntries(allowedMethods.map((method) => { - return [method, (req: NextRequest) => { + return [method, async (req: NextRequest) => { for (const [url, endpointMethods] of Object.entries(newEndpoints)) { const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); if (!match) { From f94dbe6f0136881a48d726681552f7df63984552 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 11:19:33 -0800 Subject: [PATCH 18/40] update --- apps/backend/scripts/generate-schema.ts | 27 ++++++++++++++----- .../src/route-handlers/migration-route.tsx | 5 ++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index a6b8056c6..cbc3e0045 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -106,19 +106,32 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc const fields = Object.entries((reqSchema as any).fields); - const newFields: Record = {}; - for (const key of ['body', 'query', 'body']) { + const inputFields: Record = {}; + for (const key of ['body', 'query']) { const field = fields.find(([k]) => k === key); if (field) { - newFields[key] = field[1]; + inputFields[key] = field[1]; } } - newFields['response'] = resSchema; + const outputFields: Record = {}; + for (const key of ['statusCode', 'headers', 'body']) { + const field = fields.find(([k]) => k === key); + if (field) { + outputFields[key] = field[1]; + } + } - const copiedSchema = { ...reqSchema }; - (copiedSchema as any).fields = newFields; - return schemaToTypeString(copiedSchema); + return `{ + input: ${schemaToTypeString({ + type: 'object', + fields: inputFields, + })}, + output: ${schemaToTypeString({ + type: 'object', + fields: outputFields, + })} + }`; } function schemaToTypeString(schema: yup.SchemaFieldDescription): string { diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index feb7e2f9a..4832e109d 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -24,9 +24,8 @@ type EndpointsSchema = { [url: string]: { [method in (typeof allowedMethods)[number]]?: { [overload: string]: { - query: yup.Schema, - body: yup.Schema, - response: yup.Schema, + input: EndpointInputSchema, + output: EndpointOutputSchema, }, } }, From 8ee10b25645551ba5cf29323b6edaa42e6a19423 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 12:11:55 -0800 Subject: [PATCH 19/40] added create endpoint handlers from newest endpoints --- .../src/route-handlers/migration-route.tsx | 72 ++++++++++++++----- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 4832e109d..17953756c 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -1,6 +1,6 @@ import { yupValidate } from "@stackframe/stack-shared/dist/schema-fields"; import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto"; -import { pick, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; +import { pick, typedEntries, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; import { NextRequest, NextResponse } from "next/server"; import * as yup from "yup"; import { createSmartRequest } from "./smart-request"; @@ -39,6 +39,12 @@ type EndpointHandlers = { }, }; +type NewestEndpoints = { + [url: string]: { + [method in (typeof allowedMethods)[number]]?: (req: NextRequest) => Promise + }, +} + function urlMatch(url: string, nextPattern: string): Record | null { const keys: string[] = []; @@ -205,22 +211,56 @@ async function convertRawToParsedResponse(res: NextResponse, schema: EndpointOut } } -export function createMigrationRoute(versionsSchema: EndpointsSchema[]) { - return typedFromEntries(allowedMethods.map((method) => { - return [method, async (req: NextRequest) => { - for (const [url, endpointMethods] of Object.entries(newEndpoints)) { - const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); - if (!match) { - return new NextResponse(null, { status: 404 }); - } else { - if (endpointMethods[method]) { - return NextResponse.json({ match, url: req.url, nextUrl: url }); - } else { - return new NextResponse(null, { status: 405 }); - } +// export function createMigrationRoute(versionsSchema: EndpointsSchema[]) { +// return typedFromEntries(allowedMethods.map((method) => { +// return [method, async (req: NextRequest) => { +// for (const [url, endpointMethods] of Object.entries(newEndpoints)) { +// const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); +// if (!match) { +// return new NextResponse(null, { status: 404 }); +// } else { +// if (endpointMethods[method]) { +// return NextResponse.json({ match, url: req.url, nextUrl: url }); +// } else { +// return new NextResponse(null, { status: 405 }); +// } +// } +// } +// }]; +// })); +// } + +export function createEndpointHandlersFromNewestEndpoints(newestEndpoints: NewestEndpoints, endpointsSchema: EndpointsSchema) { + const endpointHandlers: EndpointHandlers = {}; + + for (const [url, endpointMethods] of typedEntries(endpointsSchema)) { + const urlHandlers: EndpointHandlers[string] = {}; + + for (const [method, overloads] of typedEntries(endpointMethods)) { + if (!overloads) continue; + + const methodHandlers: EndpointHandlers[string][typeof allowedMethods[number]] = {}; + + for (const [overload, { input, output }] of typedEntries(overloads)) { + const endpoint = newestEndpoints[url][method]; + + if (!endpoint) { + throw new Error(`No endpoint found for ${method} ${url}`); } + + methodHandlers[overload] = async (req: ParsedRequest): Promise> => { + const rawRequest = await convertParsedRequestToRaw(req); + const rawResponse = await endpoint(rawRequest); + return await convertRawToParsedResponse(rawResponse, output); + }; } - }]; - })); + + urlHandlers[method] = methodHandlers; + } + + endpointHandlers[url] = urlHandlers; + } + + return endpointHandlers; } From 4561f58dd8e5c21618de88989662721706e30e4a Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 14:16:14 -0800 Subject: [PATCH 20/40] fixed types --- .../src/route-handlers/migration-route.tsx | 79 +++++++++++-------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 17953756c..80e894dbe 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -9,29 +9,29 @@ import { createResponse } from "./smart-response"; const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; -type EndpointInputSchema = { - query: yup.Schema, - body: yup.Schema, +export type EndpointInputSchema = { + query: Query, + body: Body, }; -type EndpointOutputSchema = { - statusCode: yup.Schema, - headers: yup.Schema, - body: yup.Schema, +export type EndpointOutputSchema = { + statusCode: StatusCode, + headers: Headers, + body: Body, }; -type EndpointsSchema = { +export type EndpointsSchema = { [url: string]: { [method in (typeof allowedMethods)[number]]?: { [overload: string]: { - input: EndpointInputSchema, - output: EndpointOutputSchema, + input: EndpointInputSchema, + output: EndpointOutputSchema, }, } }, }; -type EndpointHandlers = { +export type EndpointHandlers = { [url: string]: { [method in (typeof allowedMethods)[number]]?: { [overload: string]: (req: ParsedRequest, options?: { params: Promise> }) => Promise>, @@ -39,7 +39,7 @@ type EndpointHandlers = { }, }; -type NewestEndpoints = { +export type RawEndpointsHandlers = { [url: string]: { [method in (typeof allowedMethods)[number]]?: (req: NextRequest) => Promise }, @@ -121,13 +121,13 @@ type ParsedResponse = { } ) -async function convertRawToParsedRequest( +async function convertRawToParsedRequest( req: NextRequest, - schema: EndpointInputSchema, + schema: EndpointInputSchema, options?: { params: Promise> } ): Promise, - yup.InferType + yup.InferType, + yup.InferType >> { const bodyBuffer = await req.arrayBuffer(); const smartRequest = await createSmartRequest(req, bodyBuffer, options); @@ -162,7 +162,10 @@ async function convertParsedResponseToRaw(req: NextRequest, response: ParsedResp return await createResponse(req, requestId, response, schema); } -async function convertRawToParsedResponse(res: NextResponse, schema: EndpointOutputSchema): Promise>> { +async function convertRawToParsedResponse( + res: NextResponse, + schema: EndpointOutputSchema +): Promise>> { // TODO validate schema let contentType = res.headers.get("content-type"); if (contentType) { @@ -230,37 +233,51 @@ async function convertRawToParsedResponse(res: NextResponse, schema: EndpointOut // })); // } -export function createEndpointHandlersFromNewestEndpoints(newestEndpoints: NewestEndpoints, endpointsSchema: EndpointsSchema) { - const endpointHandlers: EndpointHandlers = {}; +type ExtractSchema = NonNullable[O][T] + +export function createEndpointHandlersFromRawEndpoints(rawEndpointHandlers: H, endpointsSchema: S): { + [url in keyof S]: { + [method in (typeof allowedMethods)[number]]: { + [overload in keyof S[url][method]]: ( + req: ParsedRequest< + yup.InferType['body']>, + yup.InferType['query']> + >, + options?: { params: Promise> } + ) => Promise['body']>>> + } + } +} { + const endpointHandlers = {}; for (const [url, endpointMethods] of typedEntries(endpointsSchema)) { - const urlHandlers: EndpointHandlers[string] = {}; + const urlHandlers = {}; for (const [method, overloads] of typedEntries(endpointMethods)) { if (!overloads) continue; - const methodHandlers: EndpointHandlers[string][typeof allowedMethods[number]] = {}; + const methodHandlers = {}; - for (const [overload, { input, output }] of typedEntries(overloads)) { - const endpoint = newestEndpoints[url][method]; + for (const [overload, endpointSchema] of typedEntries(overloads)) { + const endpoint = (rawEndpointHandlers as any)[url]?.[method]; if (!endpoint) { - throw new Error(`No endpoint found for ${method} ${url}`); + throw new Error(`No endpoint found for ${method.toString()} ${url.toString()}`); } - methodHandlers[overload] = async (req: ParsedRequest): Promise> => { + (methodHandlers as any)[overload] = async (req: ParsedRequest): Promise> => { + // TODO: validate input and output const rawRequest = await convertParsedRequestToRaw(req); - const rawResponse = await endpoint(rawRequest); - return await convertRawToParsedResponse(rawResponse, output); + const rawResponse = await (endpoint as any)(rawRequest); + return await convertRawToParsedResponse(rawResponse, (endpointSchema as any).output); }; } - urlHandlers[method] = methodHandlers; + (urlHandlers as any)[method] = methodHandlers; } - endpointHandlers[url] = urlHandlers; + (endpointHandlers as any)[url] = urlHandlers; } - return endpointHandlers; + return endpointHandlers as any; } - From 37e2104adb152845f0b03c83e18fb8d125daa2f0 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 16:09:09 -0800 Subject: [PATCH 21/40] added test file --- apps/backend/scripts/test.ts | 52 +++++++++++++++++++ .../backend/src/app/api/v2/[...all]/route.tsx | 19 ++----- 2 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 apps/backend/scripts/test.ts diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts new file mode 100644 index 000000000..07cd914e7 --- /dev/null +++ b/apps/backend/scripts/test.ts @@ -0,0 +1,52 @@ +import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints } from "@/route-handlers/migration-route"; +import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; +import { NextRequest, NextResponse } from "next/server"; + +const exampleSchema = { + '/asdf': { + 'GET': { + 'asdf': { + input: { + query: yupObject({ + abc: yupString(), + }), + body: yupObject({ + def: yupString(), + }), + }, + output: { + statusCode: yupNumber(), + headers: yupObject({}), + body: yupObject({}), + }, + }, + }, + }, +} as const satisfies EndpointsSchema; + +const exampleRawEndpointHandlers = { + '/asdf': { + 'GET': async (req: NextRequest) => { + return NextResponse.json({ 'hi': 'asdf' }); + }, + }, +} satisfies RawEndpointsHandlers; + +const exampleEndpointHandlers = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, exampleSchema); + +exampleEndpointHandlers['/asdf']['GET']['asdf']({ + url: 'http://localhost:3000/asdf', + method: 'POST', + headers: {}, + body: { + def: 'asdf', + }, + query: { + abc: 'asdf', + }, +}).then((res) => { + console.log(res, '!!!!!!!!!!!!'); +}).catch((err) => { + console.error(err); +}); + diff --git a/apps/backend/src/app/api/v2/[...all]/route.tsx b/apps/backend/src/app/api/v2/[...all]/route.tsx index 0802f0bc5..686ec5587 100644 --- a/apps/backend/src/app/api/v2/[...all]/route.tsx +++ b/apps/backend/src/app/api/v2/[...all]/route.tsx @@ -1,16 +1,5 @@ -import { NextResponse } from 'next/server'; -import { createMigrationRoute } from '../../../../route-handlers/migration-route'; +import { NextRequest, NextResponse } from "next/server"; -const route = createMigrationRoute({ - '/api/v1/users': { - GET: async (req) => { - return NextResponse.json({ 'hi': 'asdf' }); - }, - }, -}); - -export const GET = route.GET; -export const POST = route.POST; -export const PUT = route.PUT; -export const DELETE = route.DELETE; -export const PATCH = route.PATCH; +export const GET = async (req: NextRequest) => { + return NextResponse.json({ 'hi': 'asdf' }); +}; From 37934c5c5d373066fd18bd00d7703a8625a87d42 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 16:49:05 -0800 Subject: [PATCH 22/40] fixed migration route --- .../src/route-handlers/migration-route.tsx | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 80e894dbe..9d1b308ae 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -45,6 +45,20 @@ export type RawEndpointsHandlers = { }, } +type EndpointHandlersFromSchema = { + [url in keyof S]: { + [method in (typeof allowedMethods)[number]]: { + [overload in keyof S[url][method]]: ( + req: ParsedRequest< + yup.InferType['body']>, + yup.InferType['query']> + >, + options?: { params: Promise> } + ) => Promise['body']>>> + } + } +} + function urlMatch(url: string, nextPattern: string): Record | null { const keys: string[] = []; @@ -214,40 +228,35 @@ async function convertRawToParsedResponse { -// return [method, async (req: NextRequest) => { -// for (const [url, endpointMethods] of Object.entries(newEndpoints)) { -// const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); -// if (!match) { -// return new NextResponse(null, { status: 404 }); -// } else { -// if (endpointMethods[method]) { -// return NextResponse.json({ match, url: req.url, nextUrl: url }); -// } else { -// return new NextResponse(null, { status: 405 }); -// } -// } -// } -// }]; -// })); -// } +export function createMigrationEndpointHandlers( + oldEndpointsSchema: S, + newEndpointsHandlers: E, +): EndpointHandlersFromSchema { + return null as any; +} + +export function createMigrationRoute(endpointHandlers: EndpointHandlers) { + return typedFromEntries(allowedMethods.map((method) => { + return [method, async (req: NextRequest) => { + for (const [url, endpointMethods] of Object.entries(endpointHandlers)) { + const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); + if (!match) { + return new NextResponse(null, { status: 404 }); + } else { + if (endpointMethods[method]) { + return NextResponse.json({ match, url: req.url, nextUrl: url }); + } else { + return new NextResponse(null, { status: 405 }); + } + } + } + }]; + })); +} type ExtractSchema = NonNullable[O][T] -export function createEndpointHandlersFromRawEndpoints(rawEndpointHandlers: H, endpointsSchema: S): { - [url in keyof S]: { - [method in (typeof allowedMethods)[number]]: { - [overload in keyof S[url][method]]: ( - req: ParsedRequest< - yup.InferType['body']>, - yup.InferType['query']> - >, - options?: { params: Promise> } - ) => Promise['body']>>> - } - } -} { +export function createEndpointHandlersFromRawEndpoints(rawEndpointHandlers: H, endpointsSchema: S): EndpointHandlersFromSchema { const endpointHandlers = {}; for (const [url, endpointMethods] of typedEntries(endpointsSchema)) { From 5d64a7e9707705cd4158532799fbfcdfa27297bb Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 16:51:39 -0800 Subject: [PATCH 23/40] updated code format --- .../src/route-handlers/migration-route.tsx | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index 9d1b308ae..a6fe19aef 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -9,12 +9,19 @@ import { createResponse } from "./smart-response"; const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; -export type EndpointInputSchema = { +export type EndpointInputSchema< + Query extends yup.Schema, + Body extends yup.Schema +> = { query: Query, body: Body, }; -export type EndpointOutputSchema = { +export type EndpointOutputSchema< + StatusCode extends yup.Schema, + Headers extends yup.Schema, + Body extends yup.Schema +> = { statusCode: StatusCode, headers: Headers, body: Body, @@ -171,12 +178,20 @@ async function convertParsedRequestToRaw(req: ParsedRequest, schema: yup.Schema): Promise { +async function convertParsedResponseToRaw( + req: NextRequest, + response: ParsedResponse, + schema: yup.Schema +): Promise { const requestId = generateSecureRandomString(80); return await createResponse(req, requestId, response, schema); } -async function convertRawToParsedResponse( +async function convertRawToParsedResponse< + Body extends yup.Schema, + StatusCode extends yup.Schema, + Headers extends yup.Schema +>( res: NextResponse, schema: EndpointOutputSchema ): Promise>> { @@ -254,9 +269,19 @@ export function createMigrationRoute(endpointHandlers: EndpointHandlers) { })); } -type ExtractSchema = NonNullable[O][T] - -export function createEndpointHandlersFromRawEndpoints(rawEndpointHandlers: H, endpointsSchema: S): EndpointHandlersFromSchema { +type ExtractSchema< +S extends EndpointsSchema, +U extends keyof S, +M extends typeof allowedMethods[number], +O extends keyof S[U][M], +T extends 'input' | 'output' +> = NonNullable[O][T] + +export function createEndpointHandlersFromRawEndpoints< + H extends RawEndpointsHandlers, + S extends EndpointsSchema, + E extends EndpointHandlersFromSchema +>(rawEndpointHandlers: H, endpointsSchema: S): E { const endpointHandlers = {}; for (const [url, endpointMethods] of typedEntries(endpointsSchema)) { From 4b1599c31a9e6c64d2c883383ba26e758fea2f3d Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 16:53:13 -0800 Subject: [PATCH 24/40] updated small things --- apps/backend/scripts/test.ts | 2 +- apps/backend/src/route-handlers/migration-route.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index 07cd914e7..0e9b5747f 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -45,7 +45,7 @@ exampleEndpointHandlers['/asdf']['GET']['asdf']({ abc: 'asdf', }, }).then((res) => { - console.log(res, '!!!!!!!!!!!!'); + console.log(res); }).catch((err) => { console.error(err); }); diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-route.tsx index a6fe19aef..4774872dd 100644 --- a/apps/backend/src/route-handlers/migration-route.tsx +++ b/apps/backend/src/route-handlers/migration-route.tsx @@ -270,11 +270,11 @@ export function createMigrationRoute(endpointHandlers: EndpointHandlers) { } type ExtractSchema< -S extends EndpointsSchema, -U extends keyof S, -M extends typeof allowedMethods[number], -O extends keyof S[U][M], -T extends 'input' | 'output' + S extends EndpointsSchema, + U extends keyof S, + M extends typeof allowedMethods[number], + O extends keyof S[U][M], + T extends 'input' | 'output' > = NonNullable[O][T] export function createEndpointHandlersFromRawEndpoints< From b348299893b7ab2b9eea29316e0dc3553f5f3833 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 16:55:20 -0800 Subject: [PATCH 25/40] renamed file --- apps/backend/scripts/test.ts | 2 +- .../{migration-route.tsx => migration-handler.tsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/backend/src/route-handlers/{migration-route.tsx => migration-handler.tsx} (100%) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index 0e9b5747f..d7cf8a3fd 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,4 +1,4 @@ -import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints } from "@/route-handlers/migration-route"; +import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints } from "@/route-handlers/migration-handler"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; diff --git a/apps/backend/src/route-handlers/migration-route.tsx b/apps/backend/src/route-handlers/migration-handler.tsx similarity index 100% rename from apps/backend/src/route-handlers/migration-route.tsx rename to apps/backend/src/route-handlers/migration-handler.tsx From c121c03306466692070d0ed276cddb9fcf731c59 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 18:04:06 -0800 Subject: [PATCH 26/40] improved code structure --- .../src/route-handlers/migration-handler.tsx | 146 +++++++++++++----- 1 file changed, 104 insertions(+), 42 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 4774872dd..a2cbd99dc 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -52,16 +52,55 @@ export type RawEndpointsHandlers = { }, } +type ExtractInputOutputFromEndpointsSchema< + S extends EndpointsSchema, + U extends keyof S, + M extends typeof allowedMethods[number], + O extends keyof S[U][M], +> = NonNullable[O] + +type ParsedRequestFromSchema< + S extends EndpointsSchema, + url extends keyof S, + method extends (typeof allowedMethods)[number], + overload extends keyof S[url][method] +> = ParsedRequest< + yup.InferType['input']['body']>, + yup.InferType['input']['query']> +> + +type ParsedResponseFromSchema< + S extends EndpointsSchema, + url extends keyof S, + method extends (typeof allowedMethods)[number], + overload extends keyof S[url][method] +> = ParsedResponse['output']['body']>> + type EndpointHandlersFromSchema = { [url in keyof S]: { [method in (typeof allowedMethods)[number]]: { [overload in keyof S[url][method]]: ( - req: ParsedRequest< - yup.InferType['body']>, - yup.InferType['query']> - >, + req: ParsedRequestFromSchema, options?: { params: Promise> } - ) => Promise['body']>>> + ) => Promise> + } + } +} + +type EndpointTransforms< + S extends EndpointsSchema, // old endpoints schema + N extends EndpointsSchema & { [K in keyof S]: any }, // new endpoints schema + H extends EndpointHandlers, // new endpoints handlers +> = { + [url in keyof S]: { + [method in (typeof allowedMethods)[number]]: { + [overload in keyof S[url][method]]: S[url][method][overload] extends N[url][method][overload] + ? never + : (options: { + req: ParsedRequestFromSchema, + options?: { params: Promise> }, + newEndpointHandlers: H, + }) => Promise> } } } @@ -243,17 +282,11 @@ async function convertRawToParsedResponse< } } -export function createMigrationEndpointHandlers( - oldEndpointsSchema: S, - newEndpointsHandlers: E, -): EndpointHandlersFromSchema { - return null as any; -} - export function createMigrationRoute(endpointHandlers: EndpointHandlers) { return typedFromEntries(allowedMethods.map((method) => { return [method, async (req: NextRequest) => { for (const [url, endpointMethods] of Object.entries(endpointHandlers)) { + // TODO fix this const match = urlMatch(new URL(req.url).pathname.replace('v2', 'v1'), url); if (!match) { return new NextResponse(null, { status: 404 }); @@ -269,49 +302,78 @@ export function createMigrationRoute(endpointHandlers: EndpointHandlers) { })); } -type ExtractSchema< - S extends EndpointsSchema, - U extends keyof S, - M extends typeof allowedMethods[number], - O extends keyof S[U][M], - T extends 'input' | 'output' -> = NonNullable[O][T] - -export function createEndpointHandlersFromRawEndpoints< - H extends RawEndpointsHandlers, +function createEndpointHandlers< S extends EndpointsSchema, - E extends EndpointHandlersFromSchema ->(rawEndpointHandlers: H, endpointsSchema: S): E { +>( + oldEndpointsSchema: S, + getHandler: ( + url: string, + method: typeof allowedMethods[number], + overload: string, + endpointSchema: EndpointInputSchema + ) => ( + req: ParsedRequest, + options?: { params: Promise> } + ) => Promise>, +) { const endpointHandlers = {}; - - for (const [url, endpointMethods] of typedEntries(endpointsSchema)) { + for (const [url, endpointMethods] of typedEntries(oldEndpointsSchema)) { const urlHandlers = {}; - for (const [method, overloads] of typedEntries(endpointMethods)) { if (!overloads) continue; const methodHandlers = {}; - for (const [overload, endpointSchema] of typedEntries(overloads)) { - const endpoint = (rawEndpointHandlers as any)[url]?.[method]; - - if (!endpoint) { - throw new Error(`No endpoint found for ${method.toString()} ${url.toString()}`); - } - - (methodHandlers as any)[overload] = async (req: ParsedRequest): Promise> => { - // TODO: validate input and output - const rawRequest = await convertParsedRequestToRaw(req); - const rawResponse = await (endpoint as any)(rawRequest); - return await convertRawToParsedResponse(rawResponse, (endpointSchema as any).output); - }; + (methodHandlers as any)[overload] = getHandler( + url as string, + method as typeof allowedMethods[number], + overload as string, + endpointSchema as EndpointInputSchema + ); } - (urlHandlers as any)[method] = methodHandlers; } - (endpointHandlers as any)[url] = urlHandlers; } return endpointHandlers as any; } + +export function createMigrationEndpointHandlers< + S extends EndpointsSchema, + N extends EndpointsSchema, + E extends EndpointHandlers, +>( + oldEndpointsSchema: S, + newEndpointsSchema: N, + newEndpointsHandlers: E, + transforms: EndpointTransforms, +): EndpointHandlersFromSchema { + return createEndpointHandlers( + oldEndpointsSchema, + (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { + return null as any; + } + ); +} + +export function createEndpointHandlersFromRawEndpoints< + H extends RawEndpointsHandlers, + S extends EndpointsSchema, + E extends EndpointHandlersFromSchema +>(rawEndpointHandlers: H, endpointsSchema: S): E { + return createEndpointHandlers( + endpointsSchema, + (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { + const endpoint = (rawEndpointHandlers as any)[url]?.[method]; + + if (!endpoint) { + throw new Error(`No endpoint found for ${method.toString()} ${url.toString()}`); + } + + const rawRequest = await convertParsedRequestToRaw(req); + const rawResponse = await endpoint(rawRequest); + return await convertRawToParsedResponse(rawResponse, (endpointSchema as any).output); + } + ); +} From bf956df8ca245e0681798e7b9b12b9fe5dbfe07a Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 18:23:42 -0800 Subject: [PATCH 27/40] added migration handler --- .../src/route-handlers/migration-handler.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index a2cbd99dc..62f8e27c8 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -352,7 +352,20 @@ export function createMigrationEndpointHandlers< return createEndpointHandlers( oldEndpointsSchema, (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { - return null as any; + // TODO add validation + let transformedRequest = req; + const transform = (transforms as any)[url]?.[method]?.[overload]; + if (transform) { + return transform({ req, newEndpointHandlers: newEndpointsHandlers }); + } else { + const endpoint = (newEndpointsHandlers as any)[url]?.[method]; + + if (!endpoint) { + throw new Error(`No endpoint found for ${method.toString()} ${url.toString()}`); + } + + return endpoint(transformedRequest); + } } ); } From 63ada0212953a358142a004ae89d7a4859a77d11 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 12 Dec 2024 19:05:46 -0800 Subject: [PATCH 28/40] added more test code --- apps/backend/scripts/test.ts | 70 ++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index d7cf8a3fd..2c9e283a9 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,23 +1,47 @@ -import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints } from "@/route-handlers/migration-handler"; +import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; -const exampleSchema = { - '/asdf': { - 'GET': { - 'asdf': { +const schema1 = { + '/users': { + 'POST': { + 'default': { input: { - query: yupObject({ - abc: yupString(), + query: yupObject({}), + body: yupObject({ + fullName: yupString(), }), + }, + output: { + statusCode: yupNumber(), + headers: yupObject({}), body: yupObject({ - def: yupString(), + fullName: yupString(), + }), + }, + }, + }, + }, +} as const satisfies EndpointsSchema; + + +const schema2 = { + '/users': { + 'POST': { + 'default': { + input: { + query: yupObject({}), + body: yupObject({ + firstName: yupString(), + lastName: yupString(), }), }, output: { statusCode: yupNumber(), headers: yupObject({}), - body: yupObject({}), + body: yupObject({ + id: yupString(), + }), }, }, }, @@ -25,25 +49,33 @@ const exampleSchema = { } as const satisfies EndpointsSchema; const exampleRawEndpointHandlers = { - '/asdf': { - 'GET': async (req: NextRequest) => { - return NextResponse.json({ 'hi': 'asdf' }); + '/users': { + 'POST': async (req: NextRequest) => { + return NextResponse.json({ id: 'example-id' }); }, }, } satisfies RawEndpointsHandlers; -const exampleEndpointHandlers = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, exampleSchema); +const endpointHandlers1 = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, schema1); +const endpointHandlers2 = createMigrationEndpointHandlers(schema1, schema2, endpointHandlers1, { + '/users': { + 'POST': { + default: (options) => { + return endpointHandlers1['/users']['POST']['default'](options); + }, + }, + }, +}); -exampleEndpointHandlers['/asdf']['GET']['asdf']({ - url: 'http://localhost:3000/asdf', + +endpointHandlers1['/users']['POST']['default']({ + url: 'http://localhost:3000/users', method: 'POST', headers: {}, body: { - def: 'asdf', - }, - query: { - abc: 'asdf', + fullName: 'Full Name', }, + query: {}, }).then((res) => { console.log(res); }).catch((err) => { From c30e215f6e61362e174fcd6261f5229556f7c91c Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Sat, 14 Dec 2024 17:49:58 -0800 Subject: [PATCH 29/40] fixed migration route type --- apps/backend/scripts/test.ts | 50 +++++++++++++++---- .../src/route-handlers/migration-handler.tsx | 32 ++++++------ 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index 2c9e283a9..ef4fc71fb 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -9,14 +9,15 @@ const schema1 = { input: { query: yupObject({}), body: yupObject({ - fullName: yupString(), + fullName: yupString().defined(), }), }, output: { statusCode: yupNumber(), - headers: yupObject({}), + bodyType: yupString().oneOf(['json']), body: yupObject({ - fullName: yupString(), + id: yupString().defined(), + fullName: yupString().defined(), }), }, }, @@ -32,15 +33,15 @@ const schema2 = { input: { query: yupObject({}), body: yupObject({ - firstName: yupString(), - lastName: yupString(), + firstName: yupString().defined(), + lastName: yupString().defined(), }), }, output: { statusCode: yupNumber(), - headers: yupObject({}), + bodyType: yupString().oneOf(['json']), body: yupObject({ - id: yupString(), + id: yupString().defined(), }), }, }, @@ -56,17 +57,44 @@ const exampleRawEndpointHandlers = { }, } satisfies RawEndpointsHandlers; -const endpointHandlers1 = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, schema1); -const endpointHandlers2 = createMigrationEndpointHandlers(schema1, schema2, endpointHandlers1, { +const endpointHandlers2 = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, schema2); +const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endpointHandlers2, { '/users': { 'POST': { - default: (options) => { - return endpointHandlers1['/users']['POST']['default'](options); + 'default': async ({ req, newEndpointHandlers }) => { + const result = await newEndpointHandlers['/users']['POST']['default']({ + body: { + firstName: req.body.fullName.split(' ')[0], + lastName: req.body.fullName.split(' ')[1], + }, + query: {}, + headers: {}, + method: 'POST', + url: 'http://localhost:3000/users', + }); + + return { + statusCode: 200, + headers: {}, + body: { + id: (result.body as any).id, + fullName: req.body.fullName, + }, + }; }, }, + 'GET': {}, + 'DELETE': {}, + 'PATCH': {}, + 'PUT': {}, + 'OPTIONS': {}, }, }); +// type x = EndpointTransforms; + +// const y = null as unknown as x; + endpointHandlers1['/users']['POST']['default']({ url: 'http://localhost:3000/users', diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 62f8e27c8..fdeb30de7 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -19,11 +19,11 @@ export type EndpointInputSchema< export type EndpointOutputSchema< StatusCode extends yup.Schema, - Headers extends yup.Schema, + BodyType extends yup.Schema, Body extends yup.Schema > = { statusCode: StatusCode, - headers: Headers, + bodyType: BodyType, body: Body, }; @@ -87,20 +87,20 @@ type EndpointHandlersFromSchema = { } } -type EndpointTransforms< - S extends EndpointsSchema, // old endpoints schema - N extends EndpointsSchema & { [K in keyof S]: any }, // new endpoints schema +export type EndpointTransforms< + O extends EndpointsSchema, // old endpoints schema + N extends EndpointsSchema, // new endpoints schema H extends EndpointHandlers, // new endpoints handlers > = { - [url in keyof S]: { + [url in keyof O & keyof N]: { [method in (typeof allowedMethods)[number]]: { - [overload in keyof S[url][method]]: S[url][method][overload] extends N[url][method][overload] - ? never + [overload in keyof O[url][method]]: O[url][method][overload] extends N[url][method][overload] + ? undefined : (options: { - req: ParsedRequestFromSchema, + req: ParsedRequestFromSchema, options?: { params: Promise> }, newEndpointHandlers: H, - }) => Promise> + }) => Promise> } } } @@ -340,15 +340,15 @@ function createEndpointHandlers< } export function createMigrationEndpointHandlers< - S extends EndpointsSchema, + O extends EndpointsSchema, N extends EndpointsSchema, - E extends EndpointHandlers, + H extends EndpointHandlers, >( - oldEndpointsSchema: S, + oldEndpointsSchema: O, newEndpointsSchema: N, - newEndpointsHandlers: E, - transforms: EndpointTransforms, -): EndpointHandlersFromSchema { + newEndpointsHandlers: H, + transforms: EndpointTransforms, +): EndpointHandlersFromSchema { return createEndpointHandlers( oldEndpointsSchema, (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { From 28f9bd6c2be7ed8301fe85556ddfac6fc13d42d6 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 16:50:40 -0800 Subject: [PATCH 30/40] fixed type --- apps/backend/scripts/test.ts | 84 +++++++++++++++++-- .../src/route-handlers/migration-handler.tsx | 33 +++++--- packages/stack-shared/src/utils/types.tsx | 3 + 3 files changed, 102 insertions(+), 18 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index ef4fc71fb..e7d7d5b80 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,4 +1,4 @@ -import { EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; +import { EndpointTransforms, EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; @@ -23,6 +23,42 @@ const schema1 = { }, }, }, + '/tokens': { + 'POST': { + 'default': { + input: { + query: yupObject({}), + body: yupObject({}), + }, + output: { + statusCode: yupNumber(), + bodyType: yupString().oneOf(['json']), + body: yupObject({}), + }, + }, + }, + }, + '/same': { + 'POST': { + 'default': { + input: { + query: yupObject({ + same: yupString().defined(), + }), + body: yupObject({ + same: yupString().defined(), + }), + }, + output: { + statusCode: yupNumber(), + bodyType: yupString().oneOf(['json']), + body: yupObject({ + same: yupString().defined(), + }), + }, + }, + }, + }, } as const satisfies EndpointsSchema; @@ -47,6 +83,27 @@ const schema2 = { }, }, }, + '/same': { + 'POST': { + 'default': { + input: { + query: yupObject({ + same: yupString().defined(), + }), + body: yupObject({ + same: yupString().defined(), + }), + }, + output: { + statusCode: yupNumber(), + bodyType: yupString().oneOf(['json']), + body: yupObject({ + same: yupString().defined(), + }), + }, + }, + }, + }, } as const satisfies EndpointsSchema; const exampleRawEndpointHandlers = { @@ -83,17 +140,28 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp }; }, }, - 'GET': {}, - 'DELETE': {}, - 'PATCH': {}, - 'PUT': {}, - 'OPTIONS': {}, + }, + '/tokens': { + 'POST': { + 'default': async ({ req, newEndpointHandlers }) => { + return { + statusCode: 200, + headers: {}, + body: {}, + }; + }, + }, + }, + '/same': { + 'POST': { + default: undefined, + }, }, }); -// type x = EndpointTransforms; +type x = EndpointTransforms; -// const y = null as unknown as x; +const y = null as unknown as x; endpointHandlers1['/users']['POST']['default']({ diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index fdeb30de7..070cd94c8 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -87,21 +87,34 @@ type EndpointHandlersFromSchema = { } } +type Transform< + O extends EndpointsSchema, + H extends EndpointHandlers, + url extends keyof O, + method extends (typeof allowedMethods)[number], + overload extends keyof O[url][method] +> = (options: { + req: ParsedRequestFromSchema, + options?: { params: Promise> }, + newEndpointHandlers: H, +}) => Promise> + export type EndpointTransforms< O extends EndpointsSchema, // old endpoints schema N extends EndpointsSchema, // new endpoints schema H extends EndpointHandlers, // new endpoints handlers > = { - [url in keyof O & keyof N]: { - [method in (typeof allowedMethods)[number]]: { - [overload in keyof O[url][method]]: O[url][method][overload] extends N[url][method][overload] - ? undefined - : (options: { - req: ParsedRequestFromSchema, - options?: { params: Promise> }, - newEndpointHandlers: H, - }) => Promise> - } + [url in keyof O]: { + [method in (typeof allowedMethods)[number]]: method extends keyof O[url] + ? { + [overload in keyof O[url][method]]: + url extends keyof N + ? O[url][method][overload] extends N[url][method][overload] + ? undefined + : Transform + : Transform + } + : undefined } } diff --git a/packages/stack-shared/src/utils/types.tsx b/packages/stack-shared/src/utils/types.tsx index c8aab1b75..f04864eef 100644 --- a/packages/stack-shared/src/utils/types.tsx +++ b/packages/stack-shared/src/utils/types.tsx @@ -6,3 +6,6 @@ export type NullishCoalesce = T extends null | undefined ? U : T; // distributive conditional type magic. See: https://stackoverflow.com/a/50375286 export type UnionToIntersection = (U extends any ? (x: U)=>void : never) extends ((x: infer I)=>void) ? I : never + +export type OR = A extends true ? true : (B extends true ? true : false) +export type AND = A extends true ? (B extends true ? true : false) : false; From 7260ec44c5b01c4ef908367af725635a4b6a18f9 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 17:39:18 -0800 Subject: [PATCH 31/40] improved types --- apps/backend/scripts/test.ts | 17 +++-- .../src/route-handlers/migration-handler.tsx | 75 ++++++++++--------- .../src/route-handlers/smart-request.tsx | 2 +- packages/stack-shared/src/utils/types.tsx | 3 - 4 files changed, 50 insertions(+), 47 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index e7d7d5b80..bc5b680c7 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,4 +1,5 @@ -import { EndpointTransforms, EndpointsSchema, RawEndpointsHandlers, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; +import { EndpointTransforms, EndpointsSchema, RawEndpointsHandlers, TransformFn, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; +import { allowedMethods } from "@/route-handlers/smart-request"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; @@ -114,9 +115,13 @@ const exampleRawEndpointHandlers = { }, } satisfies RawEndpointsHandlers; + +const x = {} as { [key in (typeof allowedMethods)[number]]: undefined }; + const endpointHandlers2 = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, schema2); const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endpointHandlers2, { '/users': { + ...x, 'POST': { 'default': async ({ req, newEndpointHandlers }) => { const result = await newEndpointHandlers['/users']['POST']['default']({ @@ -142,6 +147,7 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp }, }, '/tokens': { + ...x, 'POST': { 'default': async ({ req, newEndpointHandlers }) => { return { @@ -152,17 +158,16 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp }, }, }, - '/same': { - 'POST': { - default: undefined, - }, - }, }); type x = EndpointTransforms; const y = null as unknown as x; +const z = null as unknown as x['/users']['POST']['default']; + +type a = TransformFn; + endpointHandlers1['/users']['POST']['default']({ url: 'http://localhost:3000/users', diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 070cd94c8..0a3419776 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -1,14 +1,11 @@ import { yupValidate } from "@stackframe/stack-shared/dist/schema-fields"; import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto"; -import { pick, typedEntries, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; +import { FilterUndefined, pick, typedEntries, typedFromEntries } from "@stackframe/stack-shared/dist/utils/objects"; import { NextRequest, NextResponse } from "next/server"; import * as yup from "yup"; -import { createSmartRequest } from "./smart-request"; +import { allowedMethods, createSmartRequest } from "./smart-request"; import { createResponse } from "./smart-response"; - -const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; - export type EndpointInputSchema< Query extends yup.Schema, Body extends yup.Schema @@ -87,36 +84,40 @@ type EndpointHandlersFromSchema = { } } -type Transform< - O extends EndpointsSchema, - H extends EndpointHandlers, - url extends keyof O, +// given an object, return the object it self if at least one key exists, otherwise return undefined +type UndefinedIfNoKey = keyof T extends never ? undefined : T; +type FilterUndefinedAndEmpty = UndefinedIfNoKey>; + +export type TransformFn< + Old extends EndpointsSchema, + Handlers extends EndpointHandlers, + url extends keyof Old, method extends (typeof allowedMethods)[number], - overload extends keyof O[url][method] + overload extends keyof Old[url][method] > = (options: { - req: ParsedRequestFromSchema, + req: ParsedRequestFromSchema, options?: { params: Promise> }, - newEndpointHandlers: H, -}) => Promise> + newEndpointHandlers: Handlers, +}) => Promise> export type EndpointTransforms< - O extends EndpointsSchema, // old endpoints schema - N extends EndpointsSchema, // new endpoints schema - H extends EndpointHandlers, // new endpoints handlers -> = { - [url in keyof O]: { - [method in (typeof allowedMethods)[number]]: method extends keyof O[url] - ? { - [overload in keyof O[url][method]]: - url extends keyof N - ? O[url][method][overload] extends N[url][method][overload] - ? undefined - : Transform - : Transform - } + Old extends EndpointsSchema, + New extends EndpointsSchema, + Handlers extends EndpointHandlers, +> = FilterUndefinedAndEmpty<{ + [url in keyof Old]: FilterUndefinedAndEmpty<{ + [method in (typeof allowedMethods)[number]]: method extends keyof Old[url] ? + FilterUndefinedAndEmpty<{ + [overload in keyof Old[url][method]]: + url extends keyof New + ? Old[url][method][overload] extends New[url][method][overload] + ? undefined + : TransformFn + : TransformFn + }> : undefined - } -} + }> +}> function urlMatch(url: string, nextPattern: string): Record | null { const keys: string[] = []; @@ -353,15 +354,15 @@ function createEndpointHandlers< } export function createMigrationEndpointHandlers< - O extends EndpointsSchema, - N extends EndpointsSchema, - H extends EndpointHandlers, + Old extends EndpointsSchema, + New extends EndpointsSchema, + Handlers extends EndpointHandlers, >( - oldEndpointsSchema: O, - newEndpointsSchema: N, - newEndpointsHandlers: H, - transforms: EndpointTransforms, -): EndpointHandlersFromSchema { + oldEndpointsSchema: Old, + newEndpointsSchema: New, + newEndpointsHandlers: Handlers, + transforms: EndpointTransforms, +): EndpointHandlersFromSchema { return createEndpointHandlers( oldEndpointsSchema, (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { diff --git a/apps/backend/src/route-handlers/smart-request.tsx b/apps/backend/src/route-handlers/smart-request.tsx index 681989f8f..39e472fa6 100644 --- a/apps/backend/src/route-handlers/smart-request.tsx +++ b/apps/backend/src/route-handlers/smart-request.tsx @@ -15,7 +15,7 @@ import { deindent } from "@stackframe/stack-shared/dist/utils/strings"; import { NextRequest } from "next/server"; import * as yup from "yup"; -const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; +export const allowedMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const; export type SmartRequestAuth = { project: ProjectsCrud["Admin"]["Read"], diff --git a/packages/stack-shared/src/utils/types.tsx b/packages/stack-shared/src/utils/types.tsx index f04864eef..c8aab1b75 100644 --- a/packages/stack-shared/src/utils/types.tsx +++ b/packages/stack-shared/src/utils/types.tsx @@ -6,6 +6,3 @@ export type NullishCoalesce = T extends null | undefined ? U : T; // distributive conditional type magic. See: https://stackoverflow.com/a/50375286 export type UnionToIntersection = (U extends any ? (x: U)=>void : never) extends ((x: infer I)=>void) ? I : never - -export type OR = A extends true ? true : (B extends true ? true : false) -export type AND = A extends true ? (B extends true ? true : false) : false; From 82086bd9bb572ffbbc55ff511947cd393cfea694 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 21:36:24 -0800 Subject: [PATCH 32/40] new schema --- apps/backend/scripts/generate-schema.ts | 43 +- apps/backend/scripts/test.ts | 18 +- apps/backend/src/app/api/v2/imports.ts | 278 ++ apps/backend/src/app/api/v2/schema.ts | 3868 +++++++++++++++++++++++ 4 files changed, 4178 insertions(+), 29 deletions(-) create mode 100644 apps/backend/src/app/api/v2/imports.ts create mode 100644 apps/backend/src/app/api/v2/schema.ts diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index cbc3e0045..80a218f07 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -15,7 +15,6 @@ function convertUrlToJSVariable(url: string, method: string) { + method.slice(0, 1).toUpperCase() + method.slice(1).toLowerCase(); } - async function main() { console.log("Started docs schema generator"); @@ -103,34 +102,30 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc if (reqSchema.type !== 'object') { throw new Error(`Unsupported schema type: ${reqSchema.type}`); } - - const fields = Object.entries((reqSchema as any).fields); - - const inputFields: Record = {}; - for (const key of ['body', 'query']) { - const field = fields.find(([k]) => k === key); - if (field) { - inputFields[key] = field[1]; + let inputFields = "{"; + for (const key of ['body', 'query', 'params']) { + const field = Object.entries((reqSchema as any).fields).find(([k]) => k === key); + if (field && (field[1] as any).fields?.length) { + inputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; } } - - const outputFields: Record = {}; - for (const key of ['statusCode', 'headers', 'body']) { - const field = fields.find(([k]) => k === key); - if (field) { - outputFields[key] = field[1]; + inputFields += "}"; + + let outputFields = "{"; + const rawOutputFields = (resSchema as any).fields; + if (rawOutputFields) { + for (const key of ['statusCode', 'headers', 'body']) { + const field = Object.entries(rawOutputFields).find(([k]) => k === key); + if (field) { + outputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; + } } } + outputFields += "}"; - return `{ - input: ${schemaToTypeString({ - type: 'object', - fields: inputFields, - })}, - output: ${schemaToTypeString({ - type: 'object', - fields: outputFields, - })} + return `{ + input: ${inputFields}, + output: ${outputFields}, }`; } diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index bc5b680c7..dad2082f5 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,5 +1,4 @@ import { EndpointTransforms, EndpointsSchema, RawEndpointsHandlers, TransformFn, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; -import { allowedMethods } from "@/route-handlers/smart-request"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; @@ -83,6 +82,19 @@ const schema2 = { }, }, }, + 'GET': { + 'default': { + input: { + query: yupObject({}), + body: yupObject({}), + }, + output: { + statusCode: yupNumber(), + bodyType: yupString().oneOf(['json']), + body: yupObject({}), + }, + }, + }, }, '/same': { 'POST': { @@ -116,12 +128,9 @@ const exampleRawEndpointHandlers = { } satisfies RawEndpointsHandlers; -const x = {} as { [key in (typeof allowedMethods)[number]]: undefined }; - const endpointHandlers2 = createEndpointHandlersFromRawEndpoints(exampleRawEndpointHandlers, schema2); const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endpointHandlers2, { '/users': { - ...x, 'POST': { 'default': async ({ req, newEndpointHandlers }) => { const result = await newEndpointHandlers['/users']['POST']['default']({ @@ -147,7 +156,6 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp }, }, '/tokens': { - ...x, 'POST': { 'default': async ({ req, newEndpointHandlers }) => { return { diff --git a/apps/backend/src/app/api/v2/imports.ts b/apps/backend/src/app/api/v2/imports.ts new file mode 100644 index 000000000..88a22b95c --- /dev/null +++ b/apps/backend/src/app/api/v2/imports.ts @@ -0,0 +1,278 @@ +import { GET as iGet } from '../../api/v1/route'; +import { GET as iUsersGet } from '../../api/v1/users/route'; +import { POST as iUsersPost } from '../../api/v1/users/route'; +import { GET as iTeamsGet } from '../../api/v1/teams/route'; +import { POST as iTeamsPost } from '../../api/v1/teams/route'; +import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; +import { GET as iTeamPermissionDefinitionsGet } from '../../api/v1/team-permission-definitions/route'; +import { POST as iTeamPermissionDefinitionsPost } from '../../api/v1/team-permission-definitions/route'; +import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; +import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; +import { GET as iEmailTemplatesGet } from '../../api/v1/email-templates/route'; +import { GET as iContactChannelsGet } from '../../api/v1/contact-channels/route'; +import { POST as iContactChannelsPost } from '../../api/v1/contact-channels/route'; +import { POST as iCheckVersionPost } from '../../api/v1/check-version/route'; +import { POST as iCheckFeatureSupportPost } from '../../api/v1/check-feature-support/route'; +import { POST as iWebhooksSvixTokenPost } from '../../api/v1/webhooks/svix-token/route'; +import { GET as iUsersMeGet } from '../../api/v1/users/me/route'; +import { DELETE as iUsersMeDelete } from '../../api/v1/users/me/route'; +import { PATCH as iUsersMePatch } from '../../api/v1/users/me/route'; +import { GET as iUsersUserIdGet } from '../../api/v1/users/[user_id]/route'; +import { DELETE as iUsersUserIdDelete } from '../../api/v1/users/[user_id]/route'; +import { PATCH as iUsersUserIdPatch } from '../../api/v1/users/[user_id]/route'; +import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { GET as iTeamsTeamIdGet } from '../../api/v1/teams/[team_id]/route'; +import { DELETE as iTeamsTeamIdDelete } from '../../api/v1/teams/[team_id]/route'; +import { PATCH as iTeamsTeamIdPatch } from '../../api/v1/teams/[team_id]/route'; +import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; +import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; +import { GET as iProjectsCurrentGet } from '../../api/v1/projects/current/route'; +import { DELETE as iProjectsCurrentDelete } from '../../api/v1/projects/current/route'; +import { PATCH as iProjectsCurrentPatch } from '../../api/v1/projects/current/route'; +import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; +import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; +import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; +import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; +import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; +import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; +import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; +import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; +import { POST as iContactChannelsVerifyPost } from '../../api/v1/contact-channels/verify/route'; +import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; +import { POST as iAuthSessionsPost } from '../../api/v1/auth/sessions/route'; +import { POST as iTeamMembershipsTeamIdUserIdPost } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; +import { DELETE as iTeamMembershipsTeamIdUserIdDelete } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; +import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { POST as iTeamInvitationsAcceptDetailsPost } from '../../api/v1/team-invitations/accept/details/route'; +import { POST as iTeamInvitationsAcceptCheckCodePost } from '../../api/v1/team-invitations/accept/check-code/route'; +import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; +import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; +import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; +import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; +import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; +import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; +import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; +import { GET as iContactChannelsUserIdContactChannelIdGet } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; +import { DELETE as iContactChannelsUserIdContactChannelIdDelete } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; +import { PATCH as iContactChannelsUserIdContactChannelIdPatch } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; +import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; +import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; +import { POST as iAuthPasswordSignUpPost } from '../../api/v1/auth/password/sign-up/route'; +import { POST as iAuthPasswordSignInPost } from '../../api/v1/auth/password/sign-in/route'; +import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; +import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; +import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; +import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; +import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; +import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; +import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; +import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; +import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; +import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; +import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; +import { POST as iTeamPermissionsTeamIdUserIdPermissionIdPost } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; +import { DELETE as iTeamPermissionsTeamIdUserIdPermissionIdDelete } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; +import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; +import { DELETE as iIntegrationsNeonOauthProvidersOauthProviderIdDelete } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; +import { PATCH as iIntegrationsNeonOauthProvidersOauthProviderIdPatch } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; +import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; +import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; +import { POST as iIntegrationsNeonInternalConfirmPost } from '../../api/v1/integrations/neon/internal/confirm/route'; +import { GET as iIntegrationsNeonApiKeysApiKeyIdGet } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; +import { PATCH as iIntegrationsNeonApiKeysApiKeyIdPatch } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; +import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; +import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; +import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; +import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; +import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; +import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; +import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; +import { GET as iIntegrationsNeonOauthIdpRouteGet } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { POST as iIntegrationsNeonOauthIdpRoutePost } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { PUT as iIntegrationsNeonOauthIdpRoutePut } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { DELETE as iIntegrationsNeonOauthIdpRouteDelete } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { PATCH as iIntegrationsNeonOauthIdpRoutePatch } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { OPTIONS as iIntegrationsNeonOauthIdpRouteOptions } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { HEAD as iIntegrationsNeonOauthIdpRouteHead } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; +import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; + +export const endpoints = { + '/': { GET: iGet }, + '/users': { GET: iUsersGet, POST: iUsersPost }, + '/teams': { GET: iTeamsGet, POST: iTeamsPost }, + '/team-permissions': { GET: iTeamPermissionsGet }, + '/team-permission-definitions': { + GET: iTeamPermissionDefinitionsGet, + POST: iTeamPermissionDefinitionsPost, + }, + '/team-member-profiles': { GET: iTeamMemberProfilesGet }, + '/team-invitations': { GET: iTeamInvitationsGet }, + '/email-templates': { GET: iEmailTemplatesGet }, + '/contact-channels': { GET: iContactChannelsGet, POST: iContactChannelsPost }, + '/check-version': { POST: iCheckVersionPost }, + '/check-feature-support': { POST: iCheckFeatureSupportPost }, + '/webhooks/svix-token': { POST: iWebhooksSvixTokenPost }, + '/users/me': { + GET: iUsersMeGet, + DELETE: iUsersMeDelete, + PATCH: iUsersMePatch, + }, + '/users/[user_id]': { + GET: iUsersUserIdGet, + DELETE: iUsersUserIdDelete, + PATCH: iUsersUserIdPatch, + }, + '/team-permission-definitions/[permission_id]': { + DELETE: iTeamPermissionDefinitionsPermissionIdDelete, + PATCH: iTeamPermissionDefinitionsPermissionIdPatch, + }, + '/teams/[team_id]': { + GET: iTeamsTeamIdGet, + DELETE: iTeamsTeamIdDelete, + PATCH: iTeamsTeamIdPatch, + }, + '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, + '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, + '/projects/current': { + GET: iProjectsCurrentGet, + DELETE: iProjectsCurrentDelete, + PATCH: iProjectsCurrentPatch, + }, + '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, + '/internal/api-keys': { + GET: iInternalApiKeysGet, + POST: iInternalApiKeysPost, + }, + '/internal/projects': { + GET: iInternalProjectsGet, + POST: iInternalProjectsPost, + }, + '/email-templates/[type]': { + GET: iEmailTemplatesTypeGet, + DELETE: iEmailTemplatesTypeDelete, + PATCH: iEmailTemplatesTypePatch, + }, + '/contact-channels/verify': { POST: iContactChannelsVerifyPost }, + '/contact-channels/send-verification-code': { + POST: iContactChannelsSendVerificationCodePost, + }, + '/auth/sessions': { POST: iAuthSessionsPost }, + '/team-memberships/[team_id]/[user_id]': { + POST: iTeamMembershipsTeamIdUserIdPost, + DELETE: iTeamMembershipsTeamIdUserIdDelete, + }, + '/team-member-profiles/[team_id]/[user_id]': { + GET: iTeamMemberProfilesTeamIdUserIdGet, + PATCH: iTeamMemberProfilesTeamIdUserIdPatch, + }, + '/team-invitations/accept/details': { + POST: iTeamInvitationsAcceptDetailsPost, + }, + '/team-invitations/accept/check-code': { + POST: iTeamInvitationsAcceptCheckCodePost, + }, + '/internal/api-keys/[api_key_id]': { + GET: iInternalApiKeysApiKeyIdGet, + PATCH: iInternalApiKeysApiKeyIdPatch, + }, + '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, + '/integrations/neon/oauth-providers': { + GET: iIntegrationsNeonOauthProvidersGet, + POST: iIntegrationsNeonOauthProvidersPost, + }, + '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, + '/integrations/neon/api-keys': { + GET: iIntegrationsNeonApiKeysGet, + POST: iIntegrationsNeonApiKeysPost, + }, + '/contact-channels/verify/check-code': { + POST: iContactChannelsVerifyCheckCodePost, + }, + '/contact-channels/[user_id]/[contact_channel_id]': { + GET: iContactChannelsUserIdContactChannelIdGet, + DELETE: iContactChannelsUserIdContactChannelIdDelete, + PATCH: iContactChannelsUserIdContactChannelIdPatch, + }, + '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, + '/auth/password/update': { POST: iAuthPasswordUpdatePost }, + '/auth/password/sign-up': { POST: iAuthPasswordSignUpPost }, + '/auth/password/sign-in': { POST: iAuthPasswordSignInPost }, + '/auth/password/set': { POST: iAuthPasswordSetPost }, + '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, + '/auth/password/reset': { POST: iAuthPasswordResetPost }, + '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, + '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, + '/auth/passkey/initiate-passkey-registration': { + POST: iAuthPasskeyInitiatePasskeyRegistrationPost, + }, + '/auth/passkey/initiate-passkey-authentication': { + POST: iAuthPasskeyInitiatePasskeyAuthenticationPost, + }, + '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, + '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, + '/auth/oauth/token': { POST: iAuthOauthTokenPost }, + '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, + '/team-permissions/[team_id]/[user_id]/[permission_id]': { + POST: iTeamPermissionsTeamIdUserIdPermissionIdPost, + DELETE: iTeamPermissionsTeamIdUserIdPermissionIdDelete, + }, + '/integrations/neon/projects/provision': { + POST: iIntegrationsNeonProjectsProvisionPost, + }, + '/integrations/neon/oauth-providers/[oauth_provider_id]': { + DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, + PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, + }, + '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, + '/integrations/neon/oauth/authorize': { + GET: iIntegrationsNeonOauthAuthorizeGet, + }, + '/integrations/neon/internal/confirm': { + POST: iIntegrationsNeonInternalConfirmPost, + }, + '/integrations/neon/api-keys/[api_key_id]': { + GET: iIntegrationsNeonApiKeysApiKeyIdGet, + PATCH: iIntegrationsNeonApiKeysApiKeyIdPatch, + }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, + }, + '/connected-accounts/[user_id]/[provider_id]/access-token': { + POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, + }, + '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, + '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, + '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, + '/auth/oauth/callback/[provider_id]': { + GET: iAuthOauthCallbackProviderIdGet, + POST: iAuthOauthCallbackProviderIdPost, + }, + '/auth/oauth/authorize/[provider_id]': { + GET: iAuthOauthAuthorizeProviderIdGet, + }, + '/integrations/neon/projects/transfer/initiate': { + POST: iIntegrationsNeonProjectsTransferInitiatePost, + }, + '/integrations/neon/oauth/idp/[[...route]]': { + GET: iIntegrationsNeonOauthIdpRouteGet, + POST: iIntegrationsNeonOauthIdpRoutePost, + PUT: iIntegrationsNeonOauthIdpRoutePut, + DELETE: iIntegrationsNeonOauthIdpRouteDelete, + PATCH: iIntegrationsNeonOauthIdpRoutePatch, + OPTIONS: iIntegrationsNeonOauthIdpRouteOptions, + HEAD: iIntegrationsNeonOauthIdpRouteHead, + }, + '/integrations/neon/projects/transfer/confirm': { + POST: iIntegrationsNeonProjectsTransferConfirmPost, + }, + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, + }, +}; diff --git a/apps/backend/src/app/api/v2/schema.ts b/apps/backend/src/app/api/v2/schema.ts new file mode 100644 index 000000000..b77962e86 --- /dev/null +++ b/apps/backend/src/app/api/v2/schema.ts @@ -0,0 +1,3868 @@ +/* eslint-disable no-restricted-syntax */ +import { + yupObject, + yupArray, + yupTuple, + yupString, + yupNumber, + yupBoolean, + yupMixed, +} from '@stackframe/stack-shared/dist/schema-fields'; + +const endpointSchema = { + '/': { + GET: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupString().defined(), + }, + }, + }, + }, + '/users': { + GET: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/teams': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + }, + }, + '/team-permissions': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/team-permission-definitions': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, + '/team-member-profiles': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/team-invitations': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/email-templates': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/contact-channels': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/check-version': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupMixed().defined(), + }, + }, + }, + }, + '/check-feature-support': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupString().defined(), + }, + }, + }, + }, + '/webhooks/svix-token': { + POST: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ token: yupString().defined() }).defined(), + }, + }, + }, + }, + '/users/me': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + selected_team: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + display_name: yupString().nullable(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + auth_with_email: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + selected_team_id: yupString().nullable(), + requires_totp_mfa: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + }).nullable(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).nullable(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).nullable(), + }, + }, + }, + DELETE: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + selected_team: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + display_name: yupString().nullable(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + auth_with_email: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + selected_team_id: yupString().nullable(), + requires_totp_mfa: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + }).nullable(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).nullable(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).nullable(), + }, + }, + }, + }, + '/users/[user_id]': { + GET: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/team-permission-definitions/[permission_id]': { + DELETE: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, + '/teams/[team_id]': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({}).defined(), + }, + }, + }, + }, + '/team-invitations/[id]': { + DELETE: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/projects/current': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + config: yupObject({ + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + config: yupObject({ + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + }, + DELETE: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/send-code': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/internal/api-keys': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).defined(), + }, + }, + }, + }, + '/internal/projects': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + }, + }, + }, + }, + '/email-templates/[type]': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/contact-channels/verify': { + POST: { + default: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/contact-channels/send-verification-code': { + POST: { + client: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + server: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + admin: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/auth/sessions': { + POST: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-memberships/[team_id]/[user_id]': { + POST: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/team-member-profiles/[team_id]/[user_id]': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + }, + PATCH: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/details': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + team_id: yupString().defined(), + team_display_name: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/check-code': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/internal/api-keys/[api_key_id]': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + }, + '/integrations/neon/webhooks': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ secret: yupString().defined() }).defined(), + }, + }, + }, + }, + '/integrations/neon/oauth-providers': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + }, + }, + }, + }, + '/integrations/neon/oauth': { + GET: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupString().defined(), + }, + }, + }, + }, + '/integrations/neon/api-keys': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + }).defined(), + }, + }, + }, + }, + '/contact-channels/verify/check-code': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/contact-channels/[user_id]/[contact_channel_id]': { + GET: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/sessions/current': { + DELETE: { + client: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + server: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + admin: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/auth/password/update': { + POST: { + client: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + server: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + admin: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/auth/password/sign-up': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/password/sign-in': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/password/set': { + POST: { + client: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + server: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + admin: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/auth/password/send-reset-code': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), + }, + }, + }, + }, + '/auth/password/reset': { + POST: { + default: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/auth/passkey/sign-in': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/passkey/register': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ user_handle: yupString().defined() }).optional(), + }, + }, + }, + }, + '/auth/passkey/initiate-passkey-registration': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), + }, + }, + }, + }, + '/auth/passkey/initiate-passkey-authentication': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/otp/sign-in': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/auth/otp/send-sign-in-code': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + }, + }, + '/auth/oauth/token': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined(), + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, + '/auth/mfa/sign-in': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-permissions/[team_id]/[user_id]/[permission_id]': { + POST: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + }, + }, + }, + DELETE: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/integrations/neon/projects/provision': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + project_id: yupString().defined(), + super_secret_admin_key: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/integrations/neon/oauth-providers/[oauth_provider_id]': { + DELETE: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + }, + }, + }, + }, + '/integrations/neon/oauth/token': { + POST: { + default: { + input: {}, + output: {}, + }, + }, + }, + '/integrations/neon/oauth/authorize': { + GET: { + default: { + input: {}, + output: {}, + }, + }, + }, + '/integrations/neon/internal/confirm': { + POST: { + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + authorization_code: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + authorization_code: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/integrations/neon/api-keys/[api_key_id]': { + GET: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + PATCH: { + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: { + client: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + server: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + admin: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([200]) }, + }, + }, + }, + '/connected-accounts/[user_id]/[provider_id]/access-token': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([201]), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + }, + }, + '/auth/sessions/current/refresh': { + POST: { + client: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + admin: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + }, + }, + '/auth/otp/sign-in/check-code': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/auth/password/reset/check-code': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/auth/oauth/callback/[provider_id]': { + GET: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([307]), + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([307]), + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, + '/auth/oauth/authorize/[provider_id]': { + GET: { + default: { + input: {}, + output: { statusCode: yupNumber().defined().oneOf([302]) }, + }, + }, + }, + '/integrations/neon/projects/transfer/initiate': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ + confirmation_url: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/integrations/neon/oauth/idp/[[...route]]': {}, + '/integrations/neon/projects/transfer/confirm': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined().oneOf([200]), + body: yupObject({ project_id: yupString().defined() }).defined(), + }, + }, + }, + }, + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: { + default: { + input: {}, + output: { + statusCode: yupNumber().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, +}; From 6ccf271f5dd8b280b04b0a0e3760afa44e28f9c0 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 21:52:55 -0800 Subject: [PATCH 33/40] generate schema --- apps/backend/scripts/generate-schema.ts | 16 +- apps/backend/src/app/api/v2/imports.ts | 130 +- apps/backend/src/app/api/v2/schema.ts | 3236 +++++++++++++++++------ 3 files changed, 2524 insertions(+), 858 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 80a218f07..45a7b2666 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -105,7 +105,7 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc let inputFields = "{"; for (const key of ['body', 'query', 'params']) { const field = Object.entries((reqSchema as any).fields).find(([k]) => k === key); - if (field && (field[1] as any).fields?.length) { + if (field && Object.keys((field[1] as any).fields || {}).length > 0) { inputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; } } @@ -114,10 +114,20 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc let outputFields = "{"; const rawOutputFields = (resSchema as any).fields; if (rawOutputFields) { - for (const key of ['statusCode', 'headers', 'body']) { + for (const key of ['statusCode', 'bodyType', 'headers', 'body']) { const field = Object.entries(rawOutputFields).find(([k]) => k === key); if (field) { - outputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; + if (key === 'statusCode') { + outputFields += `statusCode: [${(field[1] as any).oneOf.join(',')}],`; + } else if (key === 'bodyType') { + const bodyType = (field[1] as any).oneOf; + if (bodyType.length !== 1) { + throw new Error(`Unsupported body type: ${bodyType}`); + } + outputFields += `bodyType: "${bodyType[0]}",`; + } else { + outputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; + } } } } diff --git a/apps/backend/src/app/api/v2/imports.ts b/apps/backend/src/app/api/v2/imports.ts index 88a22b95c..cf0db8b5e 100644 --- a/apps/backend/src/app/api/v2/imports.ts +++ b/apps/backend/src/app/api/v2/imports.ts @@ -6,8 +6,8 @@ import { POST as iTeamsPost } from '../../api/v1/teams/route'; import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; import { GET as iTeamPermissionDefinitionsGet } from '../../api/v1/team-permission-definitions/route'; import { POST as iTeamPermissionDefinitionsPost } from '../../api/v1/team-permission-definitions/route'; -import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; +import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; import { GET as iEmailTemplatesGet } from '../../api/v1/email-templates/route'; import { GET as iContactChannelsGet } from '../../api/v1/contact-channels/route'; import { POST as iContactChannelsPost } from '../../api/v1/contact-channels/route'; @@ -20,58 +20,58 @@ import { PATCH as iUsersMePatch } from '../../api/v1/users/me/route'; import { GET as iUsersUserIdGet } from '../../api/v1/users/[user_id]/route'; import { DELETE as iUsersUserIdDelete } from '../../api/v1/users/[user_id]/route'; import { PATCH as iUsersUserIdPatch } from '../../api/v1/users/[user_id]/route'; -import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; -import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; import { GET as iTeamsTeamIdGet } from '../../api/v1/teams/[team_id]/route'; import { DELETE as iTeamsTeamIdDelete } from '../../api/v1/teams/[team_id]/route'; import { PATCH as iTeamsTeamIdPatch } from '../../api/v1/teams/[team_id]/route'; -import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; +import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; +import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; import { GET as iProjectsCurrentGet } from '../../api/v1/projects/current/route'; import { DELETE as iProjectsCurrentDelete } from '../../api/v1/projects/current/route'; import { PATCH as iProjectsCurrentPatch } from '../../api/v1/projects/current/route'; -import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; -import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; -import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; -import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; -import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; -import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; +import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; +import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { POST as iContactChannelsVerifyPost } from '../../api/v1/contact-channels/verify/route'; import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; import { POST as iAuthSessionsPost } from '../../api/v1/auth/sessions/route'; import { POST as iTeamMembershipsTeamIdUserIdPost } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { DELETE as iTeamMembershipsTeamIdUserIdDelete } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; -import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; -import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iTeamInvitationsAcceptDetailsPost } from '../../api/v1/team-invitations/accept/details/route'; import { POST as iTeamInvitationsAcceptCheckCodePost } from '../../api/v1/team-invitations/accept/check-code/route'; -import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; -import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; +import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; -import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; import { GET as iContactChannelsUserIdContactChannelIdGet } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { DELETE as iContactChannelsUserIdContactChannelIdDelete } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { PATCH as iContactChannelsUserIdContactChannelIdPatch } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; +import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; +import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; +import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; +import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; import { POST as iAuthPasswordSignUpPost } from '../../api/v1/auth/password/sign-up/route'; import { POST as iAuthPasswordSignInPost } from '../../api/v1/auth/password/sign-in/route'; -import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; +import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; -import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; +import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; import { POST as iTeamPermissionsTeamIdUserIdPermissionIdPost } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; @@ -79,20 +79,21 @@ import { DELETE as iTeamPermissionsTeamIdUserIdPermissionIdDelete } from '../../ import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; import { DELETE as iIntegrationsNeonOauthProvidersOauthProviderIdDelete } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; import { PATCH as iIntegrationsNeonOauthProvidersOauthProviderIdPatch } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; -import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; -import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; import { POST as iIntegrationsNeonInternalConfirmPost } from '../../api/v1/integrations/neon/internal/confirm/route'; +import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; import { GET as iIntegrationsNeonApiKeysApiKeyIdGet } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; import { PATCH as iIntegrationsNeonApiKeysApiKeyIdPatch } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; -import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; +import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; +import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; +import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; +import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; -import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; -import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; +import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; import { GET as iIntegrationsNeonOauthIdpRouteGet } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { POST as iIntegrationsNeonOauthIdpRoutePost } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { PUT as iIntegrationsNeonOauthIdpRoutePut } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; @@ -100,7 +101,6 @@ import { DELETE as iIntegrationsNeonOauthIdpRouteDelete } from '../../api/v1/int import { PATCH as iIntegrationsNeonOauthIdpRoutePatch } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { OPTIONS as iIntegrationsNeonOauthIdpRouteOptions } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { HEAD as iIntegrationsNeonOauthIdpRouteHead } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; -import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; export const endpoints = { @@ -112,8 +112,8 @@ export const endpoints = { GET: iTeamPermissionDefinitionsGet, POST: iTeamPermissionDefinitionsPost, }, - '/team-member-profiles': { GET: iTeamMemberProfilesGet }, '/team-invitations': { GET: iTeamInvitationsGet }, + '/team-member-profiles': { GET: iTeamMemberProfilesGet }, '/email-templates': { GET: iEmailTemplatesGet }, '/contact-channels': { GET: iContactChannelsGet, POST: iContactChannelsPost }, '/check-version': { POST: iCheckVersionPost }, @@ -129,35 +129,30 @@ export const endpoints = { DELETE: iUsersUserIdDelete, PATCH: iUsersUserIdPatch, }, - '/team-permission-definitions/[permission_id]': { - DELETE: iTeamPermissionDefinitionsPermissionIdDelete, - PATCH: iTeamPermissionDefinitionsPermissionIdPatch, - }, '/teams/[team_id]': { GET: iTeamsTeamIdGet, DELETE: iTeamsTeamIdDelete, PATCH: iTeamsTeamIdPatch, }, - '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, + '/team-permission-definitions/[permission_id]': { + DELETE: iTeamPermissionDefinitionsPermissionIdDelete, + PATCH: iTeamPermissionDefinitionsPermissionIdPatch, + }, + '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, + '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, '/projects/current': { GET: iProjectsCurrentGet, DELETE: iProjectsCurrentDelete, PATCH: iProjectsCurrentPatch, }, - '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, - '/internal/api-keys': { - GET: iInternalApiKeysGet, - POST: iInternalApiKeysPost, - }, '/internal/projects': { GET: iInternalProjectsGet, POST: iInternalProjectsPost, }, - '/email-templates/[type]': { - GET: iEmailTemplatesTypeGet, - DELETE: iEmailTemplatesTypeDelete, - PATCH: iEmailTemplatesTypePatch, + '/internal/api-keys': { + GET: iInternalApiKeysGet, + POST: iInternalApiKeysPost, }, '/contact-channels/verify': { POST: iContactChannelsVerifyPost }, '/contact-channels/send-verification-code': { @@ -168,19 +163,15 @@ export const endpoints = { POST: iTeamMembershipsTeamIdUserIdPost, DELETE: iTeamMembershipsTeamIdUserIdDelete, }, - '/team-member-profiles/[team_id]/[user_id]': { - GET: iTeamMemberProfilesTeamIdUserIdGet, - PATCH: iTeamMemberProfilesTeamIdUserIdPatch, - }, '/team-invitations/accept/details': { POST: iTeamInvitationsAcceptDetailsPost, }, '/team-invitations/accept/check-code': { POST: iTeamInvitationsAcceptCheckCodePost, }, - '/internal/api-keys/[api_key_id]': { - GET: iInternalApiKeysApiKeyIdGet, - PATCH: iInternalApiKeysApiKeyIdPatch, + '/team-member-profiles/[team_id]/[user_id]': { + GET: iTeamMemberProfilesTeamIdUserIdGet, + PATCH: iTeamMemberProfilesTeamIdUserIdPatch, }, '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, '/integrations/neon/oauth-providers': { @@ -188,35 +179,44 @@ export const endpoints = { POST: iIntegrationsNeonOauthProvidersPost, }, '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, + '/internal/api-keys/[api_key_id]': { + GET: iInternalApiKeysApiKeyIdGet, + PATCH: iInternalApiKeysApiKeyIdPatch, + }, '/integrations/neon/api-keys': { GET: iIntegrationsNeonApiKeysGet, POST: iIntegrationsNeonApiKeysPost, }, - '/contact-channels/verify/check-code': { - POST: iContactChannelsVerifyCheckCodePost, - }, '/contact-channels/[user_id]/[contact_channel_id]': { GET: iContactChannelsUserIdContactChannelIdGet, DELETE: iContactChannelsUserIdContactChannelIdDelete, PATCH: iContactChannelsUserIdContactChannelIdPatch, }, + '/contact-channels/verify/check-code': { + POST: iContactChannelsVerifyCheckCodePost, + }, '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, + '/email-templates/[type]': { + GET: iEmailTemplatesTypeGet, + DELETE: iEmailTemplatesTypeDelete, + PATCH: iEmailTemplatesTypePatch, + }, '/auth/password/update': { POST: iAuthPasswordUpdatePost }, '/auth/password/sign-up': { POST: iAuthPasswordSignUpPost }, '/auth/password/sign-in': { POST: iAuthPasswordSignInPost }, - '/auth/password/set': { POST: iAuthPasswordSetPost }, '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, '/auth/password/reset': { POST: iAuthPasswordResetPost }, + '/auth/password/set': { POST: iAuthPasswordSetPost }, '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, - '/auth/passkey/initiate-passkey-registration': { - POST: iAuthPasskeyInitiatePasskeyRegistrationPost, - }, '/auth/passkey/initiate-passkey-authentication': { POST: iAuthPasskeyInitiatePasskeyAuthenticationPost, }, '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, + '/auth/passkey/initiate-passkey-registration': { + POST: iAuthPasskeyInitiatePasskeyRegistrationPost, + }, '/auth/oauth/token': { POST: iAuthOauthTokenPost }, '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, '/team-permissions/[team_id]/[user_id]/[permission_id]': { @@ -230,13 +230,10 @@ export const endpoints = { DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, }, - '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, - '/integrations/neon/oauth/authorize': { - GET: iIntegrationsNeonOauthAuthorizeGet, - }, '/integrations/neon/internal/confirm': { POST: iIntegrationsNeonInternalConfirmPost, }, + '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, '/integrations/neon/api-keys/[api_key_id]': { GET: iIntegrationsNeonApiKeysApiKeyIdGet, PATCH: iIntegrationsNeonApiKeysApiKeyIdPatch, @@ -244,21 +241,27 @@ export const endpoints = { '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, }, + '/integrations/neon/oauth/authorize': { + GET: iIntegrationsNeonOauthAuthorizeGet, + }, + '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, }, - '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, - '/auth/oauth/callback/[provider_id]': { - GET: iAuthOauthCallbackProviderIdGet, - POST: iAuthOauthCallbackProviderIdPost, + '/integrations/neon/projects/transfer/initiate': { + POST: iIntegrationsNeonProjectsTransferInitiatePost, }, '/auth/oauth/authorize/[provider_id]': { GET: iAuthOauthAuthorizeProviderIdGet, }, - '/integrations/neon/projects/transfer/initiate': { - POST: iIntegrationsNeonProjectsTransferInitiatePost, + '/auth/oauth/callback/[provider_id]': { + GET: iAuthOauthCallbackProviderIdGet, + POST: iAuthOauthCallbackProviderIdPost, + }, + '/integrations/neon/projects/transfer/confirm': { + POST: iIntegrationsNeonProjectsTransferConfirmPost, }, '/integrations/neon/oauth/idp/[[...route]]': { GET: iIntegrationsNeonOauthIdpRouteGet, @@ -269,9 +272,6 @@ export const endpoints = { OPTIONS: iIntegrationsNeonOauthIdpRouteOptions, HEAD: iIntegrationsNeonOauthIdpRouteHead, }, - '/integrations/neon/projects/transfer/confirm': { - POST: iIntegrationsNeonProjectsTransferConfirmPost, - }, '/auth/oauth/connected-accounts/[provider_id]/access-token': { POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, }, diff --git a/apps/backend/src/app/api/v2/schema.ts b/apps/backend/src/app/api/v2/schema.ts index b77962e86..8f6e4b36b 100644 --- a/apps/backend/src/app/api/v2/schema.ts +++ b/apps/backend/src/app/api/v2/schema.ts @@ -15,7 +15,8 @@ const endpointSchema = { default: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'text', body: yupString().defined(), }, }, @@ -24,9 +25,20 @@ const endpointSchema = { '/users': { GET: { server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -74,9 +86,20 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -126,9 +149,42 @@ const endpointSchema = { }, POST: { server: { - input: {}, + input: { + body: yupObject({ + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().nullable(), + }).defined(), + ).optional(), + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -168,9 +224,42 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().nullable(), + }).defined(), + ).optional(), + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -214,9 +303,16 @@ const endpointSchema = { '/teams': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -236,9 +332,16 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -260,9 +363,16 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -286,9 +396,22 @@ const endpointSchema = { }, POST: { client: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().defined(), + creator_user_id: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -300,9 +423,24 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().defined(), + creator_user_id: yupString().optional(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -316,9 +454,24 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().defined(), + creator_user_id: yupString().optional(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -336,9 +489,22 @@ const endpointSchema = { '/team-permissions': { GET: { client: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -356,9 +522,22 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -376,9 +555,22 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -400,9 +592,14 @@ const endpointSchema = { '/team-permission-definitions': { GET: { admin: { - input: {}, + input: { + params: yupObject({ + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -424,9 +621,21 @@ const endpointSchema = { }, POST: { admin: { - input: {}, + input: { + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), + }).defined(), + params: yupObject({ + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -437,12 +646,101 @@ const endpointSchema = { }, }, }, + '/team-invitations': { + GET: { + client: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, '/team-member-profiles': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -461,9 +759,19 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -517,9 +825,19 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -574,79 +892,24 @@ const endpointSchema = { }, }, }, - '/team-invitations': { - GET: { - client: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - server: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - }, '/email-templates': { GET: { admin: { - input: {}, + input: { + params: yupObject({ + type: yupString() + .optional() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -676,9 +939,19 @@ const endpointSchema = { '/contact-channels': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -700,9 +973,19 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -724,9 +1007,19 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -750,25 +1043,60 @@ const endpointSchema = { }, POST: { client: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + input: { body: yupObject({ user_id: yupString().defined(), - id: yupString().defined(), value: yupString().defined(), type: yupString().defined().oneOf(['email']), used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + is_primary: yupBoolean().optional(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, server: { - input: {}, + input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + user_id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -782,9 +1110,27 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + user_id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -802,9 +1148,12 @@ const endpointSchema = { '/check-version': { POST: { default: { - input: {}, + input: { + body: yupObject({ clientVersion: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupMixed().defined(), }, }, @@ -815,7 +1164,8 @@ const endpointSchema = { default: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'text', body: yupString().defined(), }, }, @@ -826,7 +1176,8 @@ const endpointSchema = { admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ token: yupString().defined() }).defined(), }, @@ -838,7 +1189,8 @@ const endpointSchema = { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ selected_team: yupObject({ @@ -875,7 +1227,8 @@ const endpointSchema = { server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -917,7 +1270,8 @@ const endpointSchema = { admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -961,7 +1315,8 @@ const endpointSchema = { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -969,7 +1324,8 @@ const endpointSchema = { server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -977,7 +1333,8 @@ const endpointSchema = { admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -985,9 +1342,20 @@ const endpointSchema = { }, PATCH: { client: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + selected_team_id: yupString().optional().nullable(), + totp_secret_base64: yupString().optional().nullable(), + otp_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ selected_team: yupObject({ @@ -1022,9 +1390,27 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1064,9 +1450,27 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1110,9 +1514,20 @@ const endpointSchema = { '/users/[user_id]': { GET: { server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1152,9 +1567,20 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1196,17 +1622,39 @@ const endpointSchema = { }, DELETE: { server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -1214,9 +1662,36 @@ const endpointSchema = { }, PATCH: { server: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1256,9 +1731,36 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional().nullable(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1299,38 +1801,19 @@ const endpointSchema = { }, }, }, - '/team-permission-definitions/[permission_id]': { - DELETE: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - PATCH: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), - }).defined(), - }, - }, - }, - }, '/teams/[team_id]': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1342,9 +1825,16 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -1358,9 +1848,16 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -1376,25 +1873,46 @@ const endpointSchema = { }, DELETE: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -1402,9 +1920,21 @@ const endpointSchema = { }, PATCH: { client: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1416,9 +1946,23 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -1432,9 +1976,23 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ created_at_millis: yupNumber().defined(), @@ -1449,13 +2007,100 @@ const endpointSchema = { }, }, }, - '/team-invitations/accept': { + '/team-permission-definitions/[permission_id]': { + DELETE: { + admin: { + input: { + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + id: yupString().optional(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), + }).defined(), + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/send-code': { POST: { - default: { - input: {}, + client: { + input: { + body: yupObject({ + team_id: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({}).defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), + }, + }, + server: { + input: { + body: yupObject({ + team_id: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: { + body: yupObject({ + team_id: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + success: yupBoolean().defined().oneOf([true]), + id: yupString().defined(), + }).defined(), }, }, }, @@ -1463,37 +2108,62 @@ const endpointSchema = { '/team-invitations/[id]': { DELETE: { client: { - input: {}, + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, server: { - input: {}, + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, }, }, + '/team-invitations/accept': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({}).defined(), + }, + }, + }, + }, '/projects/current': { GET: { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1531,7 +2201,8 @@ const endpointSchema = { server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1569,7 +2240,8 @@ const endpointSchema = { admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -1663,7 +2335,8 @@ const endpointSchema = { admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -1671,36 +2344,100 @@ const endpointSchema = { }, PATCH: { admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), + input: { body: yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), + display_name: yupString().optional(), + description: yupString().optional().nullable(), + is_production_mode: yupBoolean().optional(), config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', + sign_up_enabled: yupBoolean().optional(), + credential_enabled: yupBoolean().optional(), + magic_link_enabled: yupBoolean().optional(), + passkey_enabled: yupBoolean().optional(), + client_team_creation_enabled: yupBoolean().optional(), + client_user_deletion_enabled: yupBoolean().optional(), + legacy_global_jwt_signing: yupBoolean().optional(), + allow_localhost: yupBoolean().optional(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).optional(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).optional(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).optional(), + create_team_on_sign_up: yupBoolean().optional(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + }).optional(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', 'facebook', 'discord', 'gitlab', @@ -1764,99 +2501,15 @@ const endpointSchema = { }, }, }, - '/team-invitations/send-code': { - POST: { - client: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - success: yupBoolean().defined().oneOf([true]), - id: yupString().defined(), - }).defined(), - }, - }, - server: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - success: yupBoolean().defined().oneOf([true]), - id: yupString().defined(), - }).defined(), - }, - }, - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - success: yupBoolean().defined().oneOf([true]), - id: yupString().defined(), - }).defined(), - }, - }, - }, - }, - '/internal/api-keys': { - GET: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - POST: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).defined(), - }, - }, - }, - }, '/internal/projects': { GET: { client: { - input: {}, + input: { + params: yupObject({ projectId: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -1954,9 +2607,12 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + params: yupObject({ projectId: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -2054,9 +2710,12 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + params: yupObject({ projectId: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -2156,27 +2815,35 @@ const endpointSchema = { }, POST: { client: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + input: { body: yupObject({ - id: yupString().defined(), display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), + description: yupString().optional().nullable(), + is_production_mode: yupBoolean().optional(), config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), + sign_up_enabled: yupBoolean().optional(), + credential_enabled: yupBoolean().optional(), + magic_link_enabled: yupBoolean().optional(), + passkey_enabled: yupBoolean().optional(), + client_team_creation_enabled: yupBoolean().optional(), + client_user_deletion_enabled: yupBoolean().optional(), + legacy_global_jwt_signing: yupBoolean().optional(), + allow_localhost: yupBoolean().optional(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).optional(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).optional(), oauth_providers: yupArray( yupObject({ id: yupString() @@ -2201,56 +2868,21 @@ const endpointSchema = { facebook_config_id: yupString().optional(), microsoft_tenant_id: yupString().optional(), }).defined(), - ).defined(), - enabled_oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).defined(), - ).defined(), - domains: yupArray( - yupObject({ - domain: yupString().defined(), - handler_path: yupString().defined(), - }).defined(), - ).defined(), - email_config: yupObject({ - type: yupString().defined().oneOf(['shared', 'standard']), - host: yupString().optional(), - port: yupNumber().optional(), - username: yupString().optional(), - password: yupString().optional(), - sender_name: yupString().optional(), - sender_email: yupString().optional(), - }).defined(), - create_team_on_sign_up: yupBoolean().defined(), + ).optional(), + create_team_on_sign_up: yupBoolean().optional(), team_creator_default_permissions: yupArray( yupObject({ id: yupString().defined() }).defined(), - ).defined(), + ).optional(), team_member_default_permissions: yupArray( yupObject({ id: yupString().defined() }).defined(), - ).defined(), - }).defined(), + ).optional(), + }).optional(), }).defined(), + params: yupObject({ projectId: yupString().optional() }).optional(), }, - }, - server: { - input: {}, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -2339,30 +2971,95 @@ const endpointSchema = { }).defined(), }, }, - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + server: { + input: { body: yupObject({ - id: yupString().defined(), display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), + description: yupString().optional().nullable(), + is_production_mode: yupBoolean().optional(), config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), - oauth_providers: yupArray( - yupObject({ + sign_up_enabled: yupBoolean().optional(), + credential_enabled: yupBoolean().optional(), + magic_link_enabled: yupBoolean().optional(), + passkey_enabled: yupBoolean().optional(), + client_team_creation_enabled: yupBoolean().optional(), + client_user_deletion_enabled: yupBoolean().optional(), + legacy_global_jwt_signing: yupBoolean().optional(), + allow_localhost: yupBoolean().optional(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).optional(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).optional(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).optional(), + create_team_on_sign_up: yupBoolean().optional(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + }).optional(), + }).defined(), + params: yupObject({ projectId: yupString().optional() }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ id: yupString() .defined() .oneOf([ @@ -2431,59 +3128,225 @@ const endpointSchema = { }).defined(), }, }, - }, - }, - '/email-templates/[type]': { - GET: { admin: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().defined(), + description: yupString().optional().nullable(), + is_production_mode: yupBoolean().optional(), + config: yupObject({ + sign_up_enabled: yupBoolean().optional(), + credential_enabled: yupBoolean().optional(), + magic_link_enabled: yupBoolean().optional(), + passkey_enabled: yupBoolean().optional(), + client_team_creation_enabled: yupBoolean().optional(), + client_user_deletion_enabled: yupBoolean().optional(), + legacy_global_jwt_signing: yupBoolean().optional(), + allow_localhost: yupBoolean().optional(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).optional(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).optional(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).optional(), + create_team_on_sign_up: yupBoolean().optional(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).optional(), + }).optional(), + }).defined(), + params: yupObject({ projectId: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), }).defined(), }, }, }, - DELETE: { + }, + '/internal/api-keys': { + GET: { admin: { - input: {}, + input: { + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), - body: yupMixed().optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), }, }, }, - PATCH: { + POST: { admin: { - input: {}, + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), + statusCode: [200], + bodyType: 'json', body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), }).defined(), }, }, @@ -2492,33 +3355,54 @@ const endpointSchema = { '/contact-channels/verify': { POST: { default: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, '/contact-channels/send-verification-code': { POST: { client: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, '/auth/sessions': { POST: { server: { - input: {}, + input: { + body: yupObject({ + user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ refresh_token: yupString().defined(), access_token: yupString().defined(), @@ -2526,9 +3410,15 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ refresh_token: yupString().defined(), access_token: yupString().defined(), @@ -2540,9 +3430,15 @@ const endpointSchema = { '/team-memberships/[team_id]/[user_id]': { POST: { server: { - input: {}, + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ team_id: yupString().defined(), @@ -2551,9 +3447,15 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ team_id: yupString().defined(), @@ -2564,37 +3466,94 @@ const endpointSchema = { }, DELETE: { client: { - input: {}, + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, server: { - input: {}, + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, }, }, + '/team-invitations/accept/details': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + team_id: yupString().defined(), + team_display_name: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, '/team-member-profiles/[team_id]/[user_id]': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ team_id: yupString().defined(), @@ -2605,9 +3564,19 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user: yupObject({ @@ -2653,9 +3622,19 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user: yupObject({ @@ -2703,9 +3682,23 @@ const endpointSchema = { }, PATCH: { client: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ team_id: yupString().defined(), @@ -2716,9 +3709,23 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user: yupObject({ @@ -2764,9 +3771,23 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user: yupObject({ @@ -2813,91 +3834,18 @@ const endpointSchema = { }, }, }, - '/team-invitations/accept/details': { - POST: { - default: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - team_id: yupString().defined(), - team_display_name: yupString().defined(), - }).defined(), - }, - }, - }, - }, - '/team-invitations/accept/check-code': { + '/integrations/neon/webhooks': { POST: { default: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), + input: { body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + url: yupString().defined(), + description: yupString().optional(), }).defined(), }, - }, - }, - }, - '/internal/api-keys/[api_key_id]': { - GET: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - }, - }, - }, - PATCH: { - admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - headers: yupObject({}).optional(), - body: yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - }, - }, - }, - }, - '/integrations/neon/webhooks': { - POST: { - default: { - input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ secret: yupString().defined() }).defined(), }, }, @@ -2906,9 +3854,28 @@ const endpointSchema = { '/integrations/neon/oauth-providers': { GET: { admin: { - input: {}, + input: { + params: yupObject({ + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -2945,9 +3912,50 @@ const endpointSchema = { }, POST: { admin: { - input: {}, + input: { + body: yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().optional().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + params: yupObject({ + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString() @@ -2980,18 +3988,84 @@ const endpointSchema = { default: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'text', body: yupString().defined(), }, }, }, }, + '/internal/api-keys/[api_key_id]': { + GET: { + admin: { + input: { + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + description: yupString().optional(), + revoked: yupBoolean().optional().oneOf([true]), + }).defined(), + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + }, '/integrations/neon/api-keys': { GET: { admin: { - input: {}, + input: { + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -3022,9 +4096,18 @@ const endpointSchema = { }, POST: { admin: { - input: {}, + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ id: yupString().defined(), description: yupString().defined(), @@ -3039,25 +4122,22 @@ const endpointSchema = { }, }, }, - '/contact-channels/verify/check-code': { - POST: { - default: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), - }).defined(), - }, - }, - }, - }, '/contact-channels/[user_id]/[contact_channel_id]': { GET: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3071,9 +4151,19 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3085,11 +4175,21 @@ const endpointSchema = { is_primary: yupBoolean().defined(), }).defined(), }, - }, - admin: { - input: {}, + }, + admin: { + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3105,25 +4205,55 @@ const endpointSchema = { }, DELETE: { client: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, server: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -3131,9 +4261,25 @@ const endpointSchema = { }, PATCH: { client: { - input: {}, + input: { + body: yupObject({ + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3147,9 +4293,26 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3163,9 +4326,26 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ user_id: yupString().defined(), @@ -3180,44 +4360,176 @@ const endpointSchema = { }, }, }, + '/contact-channels/verify/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, '/auth/sessions/current': { DELETE: { client: { input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/email-templates/[type]': { + GET: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + content: yupMixed().optional(), + subject: yupString().optional(), + }).defined(), + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, }, }, }, '/auth/password/update': { POST: { client: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + old_password: yupString().defined(), + new_password: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + old_password: yupString().defined(), + new_password: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + old_password: yupString().defined(), + new_password: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, '/auth/password/sign-up': { POST: { client: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3226,9 +4538,16 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3237,9 +4556,16 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3252,9 +4578,15 @@ const endpointSchema = { '/auth/password/sign-in': { POST: { client: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3263,9 +4595,15 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3274,9 +4612,15 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined(), refresh_token: yupString().defined(), @@ -3286,28 +4630,18 @@ const endpointSchema = { }, }, }, - '/auth/password/set': { - POST: { - client: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, - }, - server: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, - }, - admin: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, - }, - }, - }, '/auth/password/send-reset-code': { POST: { client: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ success: yupString() .defined() @@ -3316,9 +4650,15 @@ const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ success: yupString() .defined() @@ -3327,9 +4667,15 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ success: yupString() .defined() @@ -3342,17 +4688,50 @@ const endpointSchema = { '/auth/password/reset': { POST: { default: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ + password: yupString().defined(), + code: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/password/set': { + POST: { + client: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, '/auth/passkey/sign-in': { POST: { default: { - input: {}, + input: { + body: yupObject({ + authentication_response: yupMixed().defined(), + code: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ refresh_token: yupString().defined(), access_token: yupString().defined(), @@ -3366,119 +4745,150 @@ const endpointSchema = { '/auth/passkey/register': { POST: { default: { - input: {}, + input: { + body: yupObject({ + credential: yupMixed().defined(), + code: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ user_handle: yupString().defined() }).optional(), }, }, }, }, - '/auth/passkey/initiate-passkey-registration': { + '/auth/passkey/initiate-passkey-authentication': { POST: { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ options_json: yupMixed().defined(), code: yupString().defined(), - }).optional(), + }).defined(), }, }, server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ options_json: yupMixed().defined(), code: yupString().defined(), - }).optional(), + }).defined(), }, }, admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ options_json: yupMixed().defined(), code: yupString().defined(), - }).optional(), + }).defined(), }, }, }, }, - '/auth/passkey/initiate-passkey-authentication': { + '/auth/otp/sign-in': { POST: { - client: { - input: {}, + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), }).defined(), }, }, - server: { - input: {}, + }, + }, + '/auth/otp/send-sign-in-code': { + POST: { + client: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + server: { + input: { body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), + }, }, admin: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([200]), + input: { body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), }, - }, - }, - }, - '/auth/otp/sign-in': { - POST: { - default: { - input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), - user_id: yupString().defined(), - }).defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, }, }, - '/auth/otp/send-sign-in-code': { + '/auth/passkey/initiate-passkey-registration': { POST: { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ nonce: yupString().defined() }).defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ nonce: yupString().defined() }).defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ nonce: yupString().defined() }).defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, }, @@ -3486,9 +4896,16 @@ const endpointSchema = { '/auth/oauth/token': { POST: { default: { - input: {}, + input: { + body: yupObject({ + grant_type: yupString() + .defined() + .oneOf(['authorization_code', 'refresh_token']), + }).defined(), + }, output: { - statusCode: yupNumber().defined(), + statusCode: [], + bodyType: 'json', headers: yupMixed().defined(), body: yupMixed().defined(), }, @@ -3498,9 +4915,16 @@ const endpointSchema = { '/auth/mfa/sign-in': { POST: { default: { - input: {}, + input: { + body: yupObject({ + type: yupString().defined().oneOf(['totp']), + totp: yupString().defined(), + code: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ refresh_token: yupString().defined(), access_token: yupString().defined(), @@ -3514,9 +4938,22 @@ const endpointSchema = { '/team-permissions/[team_id]/[user_id]/[permission_id]': { POST: { server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -3526,9 +4963,22 @@ const endpointSchema = { }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([201]), + statusCode: [201], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), @@ -3540,17 +4990,43 @@ const endpointSchema = { }, DELETE: { server: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + permission_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, }, admin: { - input: {}, + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + permission_id: yupString().defined(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -3560,9 +5036,12 @@ const endpointSchema = { '/integrations/neon/projects/provision': { POST: { default: { - input: {}, + input: { + body: yupObject({ display_name: yupString().defined() }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ project_id: yupString().defined(), super_secret_admin_key: yupString().defined(), @@ -3574,9 +5053,45 @@ const endpointSchema = { '/integrations/neon/oauth-providers/[oauth_provider_id]': { DELETE: { admin: { - input: {}, + input: { + body: yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), + params: yupObject({ + oauth_provider_id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'success', headers: yupObject({}).optional(), body: yupMixed().optional(), }, @@ -3584,9 +5099,35 @@ const endpointSchema = { }, PATCH: { admin: { - input: {}, + input: { + body: yupObject({ + type: yupString().optional().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + params: yupObject({ + oauth_provider_id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ id: yupString() @@ -3614,37 +5155,33 @@ const endpointSchema = { }, }, }, - '/integrations/neon/oauth/token': { - POST: { - default: { - input: {}, - output: {}, - }, - }, - }, - '/integrations/neon/oauth/authorize': { - GET: { - default: { - input: {}, - output: {}, - }, - }, - }, '/integrations/neon/internal/confirm': { POST: { server: { - input: {}, + input: { + body: yupObject({ + interaction_uid: yupString().defined(), + project_id: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ authorization_code: yupString().defined(), }).defined(), }, }, admin: { - input: {}, + input: { + body: yupObject({ + interaction_uid: yupString().defined(), + project_id: yupString().defined(), + }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ authorization_code: yupString().defined(), }).defined(), @@ -3652,12 +5189,30 @@ const endpointSchema = { }, }, }, + '/integrations/neon/oauth/token': { + POST: { + default: { + input: { + body: yupObject({ + grant_type: yupString().defined().oneOf(['authorization_code']), + code: yupString().defined(), + code_verifier: yupString().defined(), + redirect_uri: yupString().defined(), + }).defined(), + }, + output: {}, + }, + }, + }, '/integrations/neon/api-keys/[api_key_id]': { GET: { admin: { - input: {}, + input: { + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ publishable_client_key: yupObject({ @@ -3680,9 +5235,16 @@ const endpointSchema = { }, PATCH: { admin: { - input: {}, + input: { + body: yupObject({ + description: yupString().optional(), + revoked: yupBoolean().optional().oneOf([true]), + }).defined(), + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ publishable_client_key: yupObject({ @@ -3707,67 +5269,126 @@ const endpointSchema = { '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { POST: { client: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([200]) }, + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { + '/integrations/neon/oauth/authorize': { + GET: { + default: { + input: { + query: yupObject({ + client_id: yupString().defined(), + redirect_uri: yupString().defined(), + state: yupString().defined(), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined().oneOf(['S256']), + response_type: yupString().defined().oneOf(['code']), + }).defined(), + }, + output: {}, + }, + }, + }, + '/auth/sessions/current/refresh': { POST: { client: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined() }).defined(), }, }, server: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined() }).defined(), }, }, admin: { input: {}, output: { - statusCode: yupNumber().defined().oneOf([201]), - headers: yupObject({}).optional(), + statusCode: [200], + bodyType: 'json', body: yupObject({ access_token: yupString().defined() }).defined(), }, }, }, }, - '/auth/sessions/current/refresh': { + '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: { client: { - input: {}, + input: { + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ access_token: yupString().defined() }).defined(), }, }, server: { - input: {}, + input: { + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ access_token: yupString().defined() }).defined(), }, }, admin: { - input: {}, + input: { + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ access_token: yupString().defined() }).defined(), }, }, @@ -3776,9 +5397,10 @@ const endpointSchema = { '/auth/otp/sign-in/check-code': { POST: { default: { - input: {}, + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), @@ -3789,9 +5411,10 @@ const endpointSchema = { '/auth/password/reset/check-code': { POST: { default: { - input: {}, + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), @@ -3799,67 +5422,100 @@ const endpointSchema = { }, }, }, - '/auth/oauth/callback/[provider_id]': { - GET: { + '/integrations/neon/projects/transfer/initiate': { + POST: { default: { - input: {}, + input: { + body: yupObject({ project_id: yupString().defined() }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([307]), - headers: yupMixed().defined(), - body: yupMixed().defined(), + statusCode: [200], + bodyType: 'json', + body: yupObject({ + confirmation_url: yupString().defined(), + }).defined(), }, }, }, - POST: { + }, + '/auth/oauth/authorize/[provider_id]': { + GET: { default: { - input: {}, - output: { - statusCode: yupNumber().defined().oneOf([307]), - headers: yupMixed().defined(), - body: yupMixed().defined(), + input: { + query: yupObject({ + type: yupString().optional().oneOf(['authenticate', 'link']), + token: yupString().optional(), + provider_scope: yupString().optional(), + error_redirect_url: yupString().optional(), + error_redirect_uri: yupString().optional(), + after_callback_redirect_url: yupString().optional(), + client_id: yupString().defined(), + client_secret: yupString().defined(), + redirect_uri: yupString().defined(), + scope: yupString().defined(), + state: yupString().defined(), + grant_type: yupString().defined().oneOf(['authorization_code']), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined(), + response_type: yupString().defined(), + }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), }, + output: { statusCode: [302], bodyType: 'empty' }, }, }, }, - '/auth/oauth/authorize/[provider_id]': { + '/auth/oauth/callback/[provider_id]': { GET: { default: { - input: {}, - output: { statusCode: yupNumber().defined().oneOf([302]) }, + input: { + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [307], + bodyType: 'json', + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, }, }, - }, - '/integrations/neon/projects/transfer/initiate': { POST: { default: { - input: {}, + input: { + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, output: { - statusCode: yupNumber().defined().oneOf([200]), - body: yupObject({ - confirmation_url: yupString().defined(), - }).defined(), + statusCode: [307], + bodyType: 'json', + headers: yupMixed().defined(), + body: yupMixed().defined(), }, }, }, }, - '/integrations/neon/oauth/idp/[[...route]]': {}, '/integrations/neon/projects/transfer/confirm': { POST: { default: { - input: {}, + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { - statusCode: yupNumber().defined().oneOf([200]), + statusCode: [200], + bodyType: 'json', body: yupObject({ project_id: yupString().defined() }).defined(), }, }, }, }, + '/integrations/neon/oauth/idp/[[...route]]': {}, '/auth/oauth/connected-accounts/[provider_id]/access-token': { POST: { default: { - input: {}, + input: { + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, output: { - statusCode: yupNumber().defined(), + statusCode: [], + bodyType: 'json', body: yupMixed().defined(), }, }, From 65fc68200a12449e176f2785d173b726c7f31d40 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 21:56:38 -0800 Subject: [PATCH 34/40] fixed bugs --- apps/backend/scripts/generate-schema.ts | 5 +- apps/backend/src/app/api/v2/imports.ts | 156 +- apps/backend/src/app/api/v2/schema.ts | 2280 +++++++++++------------ 3 files changed, 1219 insertions(+), 1222 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index 45a7b2666..f0ac0f17e 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -21,9 +21,8 @@ async function main() { const endpoints = await listEndpoints("api/v1", false); // ========== generate schema.ts ========== - let schemaContent = '/* eslint-disable no-restricted-syntax */\n'; - schemaContent += 'import { yupObject, yupArray, yupTuple, yupString, yupNumber, yupBoolean, yupMixed } from "@stackframe/stack-shared/dist/schema-fields";\n\n'; - schemaContent += 'const endpointSchema = {'; + let schemaContent = 'import { yupObject, yupArray, yupString, yupNumber, yupBoolean, yupMixed } from "@stackframe/stack-shared/dist/schema-fields";\n\n'; + schemaContent += 'export const endpointSchema = {'; endpoints.forEach((handlersByMethod, url) => { let methodContent = '{'; diff --git a/apps/backend/src/app/api/v2/imports.ts b/apps/backend/src/app/api/v2/imports.ts index cf0db8b5e..fa13b169b 100644 --- a/apps/backend/src/app/api/v2/imports.ts +++ b/apps/backend/src/app/api/v2/imports.ts @@ -4,96 +4,94 @@ import { POST as iUsersPost } from '../../api/v1/users/route'; import { GET as iTeamsGet } from '../../api/v1/teams/route'; import { POST as iTeamsPost } from '../../api/v1/teams/route'; import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; +import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; +import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; import { GET as iTeamPermissionDefinitionsGet } from '../../api/v1/team-permission-definitions/route'; import { POST as iTeamPermissionDefinitionsPost } from '../../api/v1/team-permission-definitions/route'; -import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; -import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; import { GET as iEmailTemplatesGet } from '../../api/v1/email-templates/route'; import { GET as iContactChannelsGet } from '../../api/v1/contact-channels/route'; import { POST as iContactChannelsPost } from '../../api/v1/contact-channels/route'; import { POST as iCheckVersionPost } from '../../api/v1/check-version/route'; import { POST as iCheckFeatureSupportPost } from '../../api/v1/check-feature-support/route'; -import { POST as iWebhooksSvixTokenPost } from '../../api/v1/webhooks/svix-token/route'; import { GET as iUsersMeGet } from '../../api/v1/users/me/route'; import { DELETE as iUsersMeDelete } from '../../api/v1/users/me/route'; import { PATCH as iUsersMePatch } from '../../api/v1/users/me/route'; import { GET as iUsersUserIdGet } from '../../api/v1/users/[user_id]/route'; import { DELETE as iUsersUserIdDelete } from '../../api/v1/users/[user_id]/route'; import { PATCH as iUsersUserIdPatch } from '../../api/v1/users/[user_id]/route'; +import { POST as iWebhooksSvixTokenPost } from '../../api/v1/webhooks/svix-token/route'; import { GET as iTeamsTeamIdGet } from '../../api/v1/teams/[team_id]/route'; import { DELETE as iTeamsTeamIdDelete } from '../../api/v1/teams/[team_id]/route'; import { PATCH as iTeamsTeamIdPatch } from '../../api/v1/teams/[team_id]/route'; -import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; -import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; -import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; import { GET as iProjectsCurrentGet } from '../../api/v1/projects/current/route'; import { DELETE as iProjectsCurrentDelete } from '../../api/v1/projects/current/route'; import { PATCH as iProjectsCurrentPatch } from '../../api/v1/projects/current/route'; -import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; -import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; +import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; +import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; +import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; +import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { POST as iContactChannelsVerifyPost } from '../../api/v1/contact-channels/verify/route'; import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; +import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; +import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; import { POST as iAuthSessionsPost } from '../../api/v1/auth/sessions/route'; +import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iTeamMembershipsTeamIdUserIdPost } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { DELETE as iTeamMembershipsTeamIdUserIdDelete } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { POST as iTeamInvitationsAcceptDetailsPost } from '../../api/v1/team-invitations/accept/details/route'; import { POST as iTeamInvitationsAcceptCheckCodePost } from '../../api/v1/team-invitations/accept/check-code/route'; -import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; -import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; -import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; -import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; +import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; +import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; import { GET as iContactChannelsUserIdContactChannelIdGet } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { DELETE as iContactChannelsUserIdContactChannelIdDelete } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { PATCH as iContactChannelsUserIdContactChannelIdPatch } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; -import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; -import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; -import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; -import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; -import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; import { POST as iAuthPasswordSignUpPost } from '../../api/v1/auth/password/sign-up/route'; import { POST as iAuthPasswordSignInPost } from '../../api/v1/auth/password/sign-in/route'; +import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; -import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; -import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; +import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; -import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; -import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; +import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; -import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; +import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; +import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; import { POST as iTeamPermissionsTeamIdUserIdPermissionIdPost } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; import { DELETE as iTeamPermissionsTeamIdUserIdPermissionIdDelete } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; -import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; import { DELETE as iIntegrationsNeonOauthProvidersOauthProviderIdDelete } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; import { PATCH as iIntegrationsNeonOauthProvidersOauthProviderIdPatch } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; +import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; import { POST as iIntegrationsNeonInternalConfirmPost } from '../../api/v1/integrations/neon/internal/confirm/route'; +import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; import { GET as iIntegrationsNeonApiKeysApiKeyIdGet } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; import { PATCH as iIntegrationsNeonApiKeysApiKeyIdPatch } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; -import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; -import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; -import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; -import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; +import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; +import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; -import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; +import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; -import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; import { GET as iIntegrationsNeonOauthIdpRouteGet } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { POST as iIntegrationsNeonOauthIdpRoutePost } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { PUT as iIntegrationsNeonOauthIdpRoutePut } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; @@ -101,6 +99,8 @@ import { DELETE as iIntegrationsNeonOauthIdpRouteDelete } from '../../api/v1/int import { PATCH as iIntegrationsNeonOauthIdpRoutePatch } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { OPTIONS as iIntegrationsNeonOauthIdpRouteOptions } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { HEAD as iIntegrationsNeonOauthIdpRouteHead } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; +import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; export const endpoints = { @@ -108,17 +108,16 @@ export const endpoints = { '/users': { GET: iUsersGet, POST: iUsersPost }, '/teams': { GET: iTeamsGet, POST: iTeamsPost }, '/team-permissions': { GET: iTeamPermissionsGet }, + '/team-member-profiles': { GET: iTeamMemberProfilesGet }, + '/team-invitations': { GET: iTeamInvitationsGet }, '/team-permission-definitions': { GET: iTeamPermissionDefinitionsGet, POST: iTeamPermissionDefinitionsPost, }, - '/team-invitations': { GET: iTeamInvitationsGet }, - '/team-member-profiles': { GET: iTeamMemberProfilesGet }, '/email-templates': { GET: iEmailTemplatesGet }, '/contact-channels': { GET: iContactChannelsGet, POST: iContactChannelsPost }, '/check-version': { POST: iCheckVersionPost }, '/check-feature-support': { POST: iCheckFeatureSupportPost }, - '/webhooks/svix-token': { POST: iWebhooksSvixTokenPost }, '/users/me': { GET: iUsersMeGet, DELETE: iUsersMeDelete, @@ -129,26 +128,28 @@ export const endpoints = { DELETE: iUsersUserIdDelete, PATCH: iUsersUserIdPatch, }, + '/webhooks/svix-token': { POST: iWebhooksSvixTokenPost }, '/teams/[team_id]': { GET: iTeamsTeamIdGet, DELETE: iTeamsTeamIdDelete, PATCH: iTeamsTeamIdPatch, }, - '/team-permission-definitions/[permission_id]': { - DELETE: iTeamPermissionDefinitionsPermissionIdDelete, - PATCH: iTeamPermissionDefinitionsPermissionIdPatch, - }, '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, - '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, '/projects/current': { GET: iProjectsCurrentGet, DELETE: iProjectsCurrentDelete, PATCH: iProjectsCurrentPatch, }, - '/internal/projects': { - GET: iInternalProjectsGet, - POST: iInternalProjectsPost, + '/team-permission-definitions/[permission_id]': { + DELETE: iTeamPermissionDefinitionsPermissionIdDelete, + PATCH: iTeamPermissionDefinitionsPermissionIdPatch, + }, + '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, + '/email-templates/[type]': { + GET: iEmailTemplatesTypeGet, + DELETE: iEmailTemplatesTypeDelete, + PATCH: iEmailTemplatesTypePatch, }, '/internal/api-keys': { GET: iInternalApiKeysGet, @@ -158,7 +159,15 @@ export const endpoints = { '/contact-channels/send-verification-code': { POST: iContactChannelsSendVerificationCodePost, }, + '/internal/projects': { + GET: iInternalProjectsGet, + POST: iInternalProjectsPost, + }, '/auth/sessions': { POST: iAuthSessionsPost }, + '/team-member-profiles/[team_id]/[user_id]': { + GET: iTeamMemberProfilesTeamIdUserIdGet, + PATCH: iTeamMemberProfilesTeamIdUserIdPatch, + }, '/team-memberships/[team_id]/[user_id]': { POST: iTeamMembershipsTeamIdUserIdPost, DELETE: iTeamMembershipsTeamIdUserIdDelete, @@ -169,100 +178,85 @@ export const endpoints = { '/team-invitations/accept/check-code': { POST: iTeamInvitationsAcceptCheckCodePost, }, - '/team-member-profiles/[team_id]/[user_id]': { - GET: iTeamMemberProfilesTeamIdUserIdGet, - PATCH: iTeamMemberProfilesTeamIdUserIdPatch, - }, '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, '/integrations/neon/oauth-providers': { GET: iIntegrationsNeonOauthProvidersGet, POST: iIntegrationsNeonOauthProvidersPost, }, '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, - '/internal/api-keys/[api_key_id]': { - GET: iInternalApiKeysApiKeyIdGet, - PATCH: iInternalApiKeysApiKeyIdPatch, - }, '/integrations/neon/api-keys': { GET: iIntegrationsNeonApiKeysGet, POST: iIntegrationsNeonApiKeysPost, }, - '/contact-channels/[user_id]/[contact_channel_id]': { - GET: iContactChannelsUserIdContactChannelIdGet, - DELETE: iContactChannelsUserIdContactChannelIdDelete, - PATCH: iContactChannelsUserIdContactChannelIdPatch, + '/internal/api-keys/[api_key_id]': { + GET: iInternalApiKeysApiKeyIdGet, + PATCH: iInternalApiKeysApiKeyIdPatch, }, '/contact-channels/verify/check-code': { POST: iContactChannelsVerifyCheckCodePost, }, '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, - '/email-templates/[type]': { - GET: iEmailTemplatesTypeGet, - DELETE: iEmailTemplatesTypeDelete, - PATCH: iEmailTemplatesTypePatch, + '/contact-channels/[user_id]/[contact_channel_id]': { + GET: iContactChannelsUserIdContactChannelIdGet, + DELETE: iContactChannelsUserIdContactChannelIdDelete, + PATCH: iContactChannelsUserIdContactChannelIdPatch, }, '/auth/password/update': { POST: iAuthPasswordUpdatePost }, '/auth/password/sign-up': { POST: iAuthPasswordSignUpPost }, '/auth/password/sign-in': { POST: iAuthPasswordSignInPost }, + '/auth/password/set': { POST: iAuthPasswordSetPost }, '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, '/auth/password/reset': { POST: iAuthPasswordResetPost }, - '/auth/password/set': { POST: iAuthPasswordSetPost }, '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, - '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, + '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, '/auth/passkey/initiate-passkey-authentication': { POST: iAuthPasskeyInitiatePasskeyAuthenticationPost, }, - '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, - '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, '/auth/passkey/initiate-passkey-registration': { POST: iAuthPasskeyInitiatePasskeyRegistrationPost, }, + '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, '/auth/oauth/token': { POST: iAuthOauthTokenPost }, - '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, + '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, + '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, '/team-permissions/[team_id]/[user_id]/[permission_id]': { POST: iTeamPermissionsTeamIdUserIdPermissionIdPost, DELETE: iTeamPermissionsTeamIdUserIdPermissionIdDelete, }, - '/integrations/neon/projects/provision': { - POST: iIntegrationsNeonProjectsProvisionPost, - }, '/integrations/neon/oauth-providers/[oauth_provider_id]': { DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, }, + '/integrations/neon/projects/provision': { + POST: iIntegrationsNeonProjectsProvisionPost, + }, '/integrations/neon/internal/confirm': { POST: iIntegrationsNeonInternalConfirmPost, }, + '/integrations/neon/oauth/authorize': { + GET: iIntegrationsNeonOauthAuthorizeGet, + }, '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, '/integrations/neon/api-keys/[api_key_id]': { GET: iIntegrationsNeonApiKeysApiKeyIdGet, PATCH: iIntegrationsNeonApiKeysApiKeyIdPatch, }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { - POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, - }, - '/integrations/neon/oauth/authorize': { - GET: iIntegrationsNeonOauthAuthorizeGet, - }, - '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, }, - '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, - '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, - '/integrations/neon/projects/transfer/initiate': { - POST: iIntegrationsNeonProjectsTransferInitiatePost, + '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, }, + '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, '/auth/oauth/authorize/[provider_id]': { GET: iAuthOauthAuthorizeProviderIdGet, }, + '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, '/auth/oauth/callback/[provider_id]': { GET: iAuthOauthCallbackProviderIdGet, POST: iAuthOauthCallbackProviderIdPost, }, - '/integrations/neon/projects/transfer/confirm': { - POST: iIntegrationsNeonProjectsTransferConfirmPost, - }, '/integrations/neon/oauth/idp/[[...route]]': { GET: iIntegrationsNeonOauthIdpRouteGet, POST: iIntegrationsNeonOauthIdpRoutePost, @@ -272,6 +266,12 @@ export const endpoints = { OPTIONS: iIntegrationsNeonOauthIdpRouteOptions, HEAD: iIntegrationsNeonOauthIdpRouteHead, }, + '/integrations/neon/projects/transfer/confirm': { + POST: iIntegrationsNeonProjectsTransferConfirmPost, + }, + '/integrations/neon/projects/transfer/initiate': { + POST: iIntegrationsNeonProjectsTransferInitiatePost, + }, '/auth/oauth/connected-accounts/[provider_id]/access-token': { POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, }, diff --git a/apps/backend/src/app/api/v2/schema.ts b/apps/backend/src/app/api/v2/schema.ts index 8f6e4b36b..c4d8c9628 100644 --- a/apps/backend/src/app/api/v2/schema.ts +++ b/apps/backend/src/app/api/v2/schema.ts @@ -1,15 +1,13 @@ -/* eslint-disable no-restricted-syntax */ import { yupObject, yupArray, - yupTuple, yupString, yupNumber, yupBoolean, yupMixed, } from '@stackframe/stack-shared/dist/schema-fields'; -const endpointSchema = { +export const endpointSchema = { '/': { GET: { default: { @@ -589,142 +587,6 @@ const endpointSchema = { }, }, }, - '/team-permission-definitions': { - GET: { - admin: { - input: { - params: yupObject({ - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - POST: { - admin: { - input: { - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).optional(), - }).defined(), - params: yupObject({ - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), - }).defined(), - }, - }, - }, - }, - '/team-invitations': { - GET: { - client: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - server: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - admin: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - }, '/team-member-profiles': { GET: { client: { @@ -892,20 +754,12 @@ const endpointSchema = { }, }, }, - '/email-templates': { + '/team-invitations': { GET: { - admin: { + client: { input: { - params: yupObject({ - type: yupString() - .optional() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - }).optional(), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), }, output: { statusCode: [200], @@ -914,17 +768,10 @@ const endpointSchema = { body: yupObject({ items: yupArray( yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -934,20 +781,10 @@ const endpointSchema = { }).defined(), }, }, - }, - }, - '/contact-channels': { - GET: { - client: { + server: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), }, output: { statusCode: [200], @@ -956,13 +793,10 @@ const endpointSchema = { body: yupObject({ items: yupArray( yupObject({ - user_id: yupString().defined(), id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -972,16 +806,10 @@ const endpointSchema = { }).defined(), }, }, - server: { + admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), }, output: { statusCode: [200], @@ -990,13 +818,10 @@ const endpointSchema = { body: yupObject({ items: yupArray( yupObject({ - user_id: yupString().defined(), id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -1006,15 +831,14 @@ const endpointSchema = { }).defined(), }, }, + }, + }, + '/team-permission-definitions': { + GET: { admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), params: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), + permission_id: yupString().optional(), }).optional(), }, output: { @@ -1024,13 +848,11 @@ const endpointSchema = { body: yupObject({ items: yupArray( yupObject({ - user_id: yupString().defined(), id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -1042,22 +864,17 @@ const endpointSchema = { }, }, POST: { - client: { + admin: { input: { body: yupObject({ - user_id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_primary: yupBoolean().optional(), + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), }).defined(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), params: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), + permission_id: yupString().optional(), }).optional(), }, output: { @@ -1065,10 +882,191 @@ const endpointSchema = { bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, + '/email-templates': { + GET: { + admin: { + input: { + params: yupObject({ + type: yupString() + .optional() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/contact-channels': { + GET: { + client: { + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + client: { + input: { + body: yupObject({ + user_id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_primary: yupBoolean().optional(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), used_for_auth: yupBoolean().defined(), is_verified: yupBoolean().defined(), is_primary: yupBoolean().defined(), @@ -1171,19 +1169,6 @@ const endpointSchema = { }, }, }, - '/webhooks/svix-token': { - POST: { - admin: { - input: {}, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ token: yupString().defined() }).defined(), - }, - }, - }, - }, '/users/me': { GET: { client: { @@ -1801,6 +1786,19 @@ const endpointSchema = { }, }, }, + '/webhooks/svix-token': { + POST: { + admin: { + input: {}, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ token: yupString().defined() }).defined(), + }, + }, + }, + }, '/teams/[team_id]': { GET: { client: { @@ -2007,49 +2005,6 @@ const endpointSchema = { }, }, }, - '/team-permission-definitions/[permission_id]': { - DELETE: { - admin: { - input: { - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - PATCH: { - admin: { - input: { - body: yupObject({ - id: yupString().optional(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).optional(), - }).defined(), - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), - }).defined(), - }, - }, - }, - }, '/team-invitations/send-code': { POST: { client: { @@ -2105,46 +2060,6 @@ const endpointSchema = { }, }, }, - '/team-invitations/[id]': { - DELETE: { - client: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - server: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - admin: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - }, '/team-invitations/accept': { POST: { default: { @@ -2501,158 +2416,441 @@ const endpointSchema = { }, }, }, - '/internal/projects': { - GET: { - client: { + '/team-permission-definitions/[permission_id]': { + DELETE: { + admin: { input: { - params: yupObject({ projectId: yupString().optional() }).optional(), + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + id: yupString().optional(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), + }).defined(), + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), - config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - enabled: yupBoolean().defined(), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).defined(), - ).defined(), - enabled_oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).defined(), - ).defined(), - domains: yupArray( - yupObject({ - domain: yupString().defined(), - handler_path: yupString().defined(), - }).defined(), - ).defined(), - email_config: yupObject({ - type: yupString().defined().oneOf(['shared', 'standard']), - host: yupString().optional(), - port: yupNumber().optional(), - username: yupString().optional(), - password: yupString().optional(), - sender_name: yupString().optional(), - sender_email: yupString().optional(), - }).defined(), - create_team_on_sign_up: yupBoolean().defined(), - team_creator_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - team_member_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - }).defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), }).defined(), }, }, + }, + }, + '/team-invitations/[id]': { + DELETE: { + client: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, server: { input: { - params: yupObject({ projectId: yupString().optional() }).optional(), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/email-templates/[type]': { + GET: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), - config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - enabled: yupBoolean().defined(), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + content: yupMixed().optional(), + subject: yupString().optional(), + }).defined(), + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/internal/api-keys': { + GET: { + admin: { + input: { + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).defined(), + }, + }, + }, + }, + '/contact-channels/verify': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/contact-channels/send-verification-code': { + POST: { + client: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/internal/projects': { + GET: { + client: { + input: { + params: yupObject({ projectId: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: { + params: yupObject({ projectId: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), client_secret: yupString().optional(), facebook_config_id: yupString().optional(), microsoft_tenant_id: yupString().optional(), @@ -3270,269 +3468,54 @@ const endpointSchema = { host: yupString().optional(), port: yupNumber().optional(), username: yupString().optional(), - password: yupString().optional(), - sender_name: yupString().optional(), - sender_email: yupString().optional(), - }).defined(), - create_team_on_sign_up: yupBoolean().defined(), - team_creator_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - team_member_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - }).defined(), - }).defined(), - }, - }, - }, - }, - '/internal/api-keys': { - GET: { - admin: { - input: { - params: yupObject({ api_key_id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - POST: { - admin: { - input: { - body: yupObject({ - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - has_publishable_client_key: yupBoolean().defined(), - has_secret_server_key: yupBoolean().defined(), - has_super_secret_admin_key: yupBoolean().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).defined(), - }, - }, - }, - }, - '/contact-channels/verify': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/contact-channels/send-verification-code': { - POST: { - client: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/auth/sessions': { - POST: { - server: { - input: { - body: yupObject({ - user_id: yupString().defined(), - expires_in_millis: yupNumber().optional(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - }).defined(), - }, - }, - admin: { - input: { - body: yupObject({ - user_id: yupString().defined(), - expires_in_millis: yupNumber().optional(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), }).defined(), }, }, }, }, - '/team-memberships/[team_id]/[user_id]': { + '/auth/sessions': { POST: { server: { input: { - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), }).defined(), }, - }, - admin: { - input: { - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), + refresh_token: yupString().defined(), + access_token: yupString().defined(), }).defined(), }, }, - }, - DELETE: { - client: { - input: { - params: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - server: { - input: { - params: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, admin: { input: { - params: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - }, - '/team-invitations/accept/details': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', body: yupObject({ - team_id: yupString().defined(), - team_display_name: yupString().defined(), + user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), }).defined(), }, - }, - }, - }, - '/team-invitations/accept/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + refresh_token: yupString().defined(), + access_token: yupString().defined(), }).defined(), }, }, @@ -3834,6 +3817,117 @@ const endpointSchema = { }, }, }, + '/team-memberships/[team_id]/[user_id]': { + POST: { + server: { + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/team-invitations/accept/details': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + team_id: yupString().defined(), + team_display_name: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, '/integrations/neon/webhooks': { POST: { default: { @@ -3982,15 +4076,80 @@ const endpointSchema = { }, }, }, - }, - '/integrations/neon/oauth': { - GET: { - default: { - input: {}, + }, + '/integrations/neon/oauth': { + GET: { + default: { + input: {}, + output: { + statusCode: [200], + bodyType: 'text', + body: yupString().defined(), + }, + }, + }, + }, + '/integrations/neon/api-keys': { + GET: { + admin: { + input: { + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + }, output: { statusCode: [200], - bodyType: 'text', - body: yupString().defined(), + bodyType: 'json', + body: yupObject({ + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + }).defined(), }, }, }, @@ -4057,68 +4216,33 @@ const endpointSchema = { }, }, }, - '/integrations/neon/api-keys': { - GET: { - admin: { - input: { - params: yupObject({ api_key_id: yupString().optional() }).optional(), - }, + '/contact-channels/verify/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), + is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), }, }, }, - POST: { + }, + '/auth/sessions/current': { + DELETE: { + client: { + input: {}, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: {}, + output: { statusCode: [200], bodyType: 'success' }, + }, admin: { - input: { - body: yupObject({ - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - has_publishable_client_key: yupBoolean().defined(), - has_secret_server_key: yupBoolean().defined(), - has_super_secret_admin_key: yupBoolean().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), - }).defined(), - }, + input: {}, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, @@ -4275,194 +4399,72 @@ const endpointSchema = { params: yupObject({ user_id: yupString().defined(), contact_channel_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), - }, - }, - server: { - input: { - body: yupObject({ - is_verified: yupBoolean().optional(), - value: yupString().optional(), - type: yupString().optional().oneOf(['email']), - used_for_auth: yupBoolean().optional(), - is_primary: yupBoolean().optional(), - }).optional(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), - }, - }, - admin: { - input: { - body: yupObject({ - is_verified: yupBoolean().optional(), - value: yupString().optional(), - type: yupString().optional().oneOf(['email']), - used_for_auth: yupBoolean().optional(), - is_primary: yupBoolean().optional(), - }).optional(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), - }, - }, - }, - }, - '/contact-channels/verify/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), - }).defined(), - }, - }, - }, - }, - '/auth/sessions/current': { - DELETE: { - client: { - input: {}, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: {}, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: {}, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/email-templates/[type]': { - GET: { - admin: { - input: { - params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, - }, - DELETE: { - admin: { + server: { input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { statusCode: [200], - bodyType: 'success', + bodyType: 'json', headers: yupObject({}).optional(), - body: yupMixed().optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), }, }, - }, - PATCH: { admin: { input: { body: yupObject({ - content: yupMixed().optional(), - subject: yupString().optional(), - }).defined(), + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { @@ -4470,17 +4472,13 @@ const endpointSchema = { bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, @@ -4630,6 +4628,28 @@ const endpointSchema = { }, }, }, + '/auth/password/set': { + POST: { + client: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, '/auth/password/send-reset-code': { POST: { client: { @@ -4698,28 +4718,6 @@ const endpointSchema = { }, }, }, - '/auth/password/set': { - POST: { - client: { - input: { - body: yupObject({ password: yupString().defined() }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ password: yupString().defined() }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ password: yupString().defined() }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, '/auth/passkey/sign-in': { POST: { default: { @@ -4742,19 +4740,25 @@ const endpointSchema = { }, }, }, - '/auth/passkey/register': { + '/auth/mfa/sign-in': { POST: { default: { input: { body: yupObject({ - credential: yupMixed().defined(), + type: yupString().defined().oneOf(['totp']), + totp: yupString().defined(), code: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ user_handle: yupString().defined() }).optional(), + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), }, }, }, @@ -4796,141 +4800,135 @@ const endpointSchema = { }, }, }, - '/auth/otp/sign-in': { + '/auth/passkey/initiate-passkey-registration': { POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + client: { + input: {}, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), - user_id: yupString().defined(), - }).defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, - }, - }, - '/auth/otp/send-sign-in-code': { - POST: { - client: { - input: { + server: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, + }, + admin: { + input: {}, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, - server: { + }, + }, + '/auth/passkey/register': { + POST: { + default: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + credential: yupMixed().defined(), + code: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + body: yupObject({ user_handle: yupString().defined() }).optional(), }, }, - admin: { + }, + }, + '/auth/oauth/token': { + POST: { + default: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + grant_type: yupString() + .defined() + .oneOf(['authorization_code', 'refresh_token']), }).defined(), }, output: { - statusCode: [200], + statusCode: [], bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + headers: yupMixed().defined(), + body: yupMixed().defined(), }, }, }, }, - '/auth/passkey/initiate-passkey-registration': { + '/auth/otp/sign-in': { POST: { - client: { - input: {}, + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), }, }, - server: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', + }, + }, + '/auth/otp/send-sign-in-code': { + POST: { + client: { + input: { body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, - }, - admin: { - input: {}, output: { statusCode: [200], - bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, - }, - }, - '/auth/oauth/token': { - POST: { - default: { + server: { input: { body: yupObject({ - grant_type: yupString() - .defined() - .oneOf(['authorization_code', 'refresh_token']), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), }, output: { - statusCode: [], + statusCode: [200], bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, - }, - }, - '/auth/mfa/sign-in': { - POST: { - default: { + admin: { input: { body: yupObject({ - type: yupString().defined().oneOf(['totp']), - totp: yupString().defined(), - code: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), - user_id: yupString().defined(), - }).defined(), + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, }, @@ -5033,23 +5031,6 @@ const endpointSchema = { }, }, }, - '/integrations/neon/projects/provision': { - POST: { - default: { - input: { - body: yupObject({ display_name: yupString().defined() }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - project_id: yupString().defined(), - super_secret_admin_key: yupString().defined(), - }).defined(), - }, - }, - }, - }, '/integrations/neon/oauth-providers/[oauth_provider_id]': { DELETE: { admin: { @@ -5155,6 +5136,23 @@ const endpointSchema = { }, }, }, + '/integrations/neon/projects/provision': { + POST: { + default: { + input: { + body: yupObject({ display_name: yupString().defined() }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + project_id: yupString().defined(), + super_secret_admin_key: yupString().defined(), + }).defined(), + }, + }, + }, + }, '/integrations/neon/internal/confirm': { POST: { server: { @@ -5189,6 +5187,23 @@ const endpointSchema = { }, }, }, + '/integrations/neon/oauth/authorize': { + GET: { + default: { + input: { + query: yupObject({ + client_id: yupString().defined(), + redirect_uri: yupString().defined(), + state: yupString().defined(), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined().oneOf(['S256']), + response_type: yupString().defined().oneOf(['code']), + }).defined(), + }, + output: {}, + }, + }, + }, '/integrations/neon/oauth/token': { POST: { default: { @@ -5266,54 +5281,52 @@ const endpointSchema = { }, }, }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: { client: { input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, server: { input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/integrations/neon/oauth/authorize': { - GET: { - default: { - input: { - query: yupObject({ - client_id: yupString().defined(), - redirect_uri: yupString().defined(), - state: yupString().defined(), - code_challenge: yupString().defined(), - code_challenge_method: yupString().defined().oneOf(['S256']), - response_type: yupString().defined().oneOf(['code']), - }).defined(), + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, - output: {}, }, }, }, @@ -5345,66 +5358,37 @@ const endpointSchema = { }, }, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { POST: { client: { input: { - body: yupObject({ scope: yupString().optional() }).defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { input: { - body: yupObject({ scope: yupString().optional() }).defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { - body: yupObject({ scope: yupString().optional() }).defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/auth/otp/sign-in/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, @@ -5422,22 +5406,6 @@ const endpointSchema = { }, }, }, - '/integrations/neon/projects/transfer/initiate': { - POST: { - default: { - input: { - body: yupObject({ project_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - confirmation_url: yupString().defined(), - }).defined(), - }, - }, - }, - }, '/auth/oauth/authorize/[provider_id]': { GET: { default: { @@ -5465,6 +5433,20 @@ const endpointSchema = { }, }, }, + '/auth/otp/sign-in/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, '/auth/oauth/callback/[provider_id]': { GET: { default: { @@ -5493,6 +5475,7 @@ const endpointSchema = { }, }, }, + '/integrations/neon/oauth/idp/[[...route]]': {}, '/integrations/neon/projects/transfer/confirm': { POST: { default: { @@ -5505,7 +5488,22 @@ const endpointSchema = { }, }, }, - '/integrations/neon/oauth/idp/[[...route]]': {}, + '/integrations/neon/projects/transfer/initiate': { + POST: { + default: { + input: { + body: yupObject({ project_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + confirmation_url: yupString().defined(), + }).defined(), + }, + }, + }, + }, '/auth/oauth/connected-accounts/[provider_id]/access-token': { POST: { default: { From ec10dff4111bc55b3f3c8a5fd7d909764ab5f766 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 16 Dec 2024 22:17:41 -0800 Subject: [PATCH 35/40] updated migration handler --- .../src/route-handlers/migration-handler.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 0a3419776..3dbee81a5 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -6,6 +6,8 @@ import * as yup from "yup"; import { allowedMethods, createSmartRequest } from "./smart-request"; import { createResponse } from "./smart-response"; +type BodyType = 'json' | 'text' | 'binary' | 'success' | 'empty'; + export type EndpointInputSchema< Query extends yup.Schema, Body extends yup.Schema @@ -15,12 +17,12 @@ export type EndpointInputSchema< }; export type EndpointOutputSchema< - StatusCode extends yup.Schema, - BodyType extends yup.Schema, - Body extends yup.Schema + StatusCode extends number, + T extends BodyType, + Body extends yup.Schema | undefined > = { statusCode: StatusCode, - bodyType: BodyType, + bodyType: T, body: Body, }; @@ -242,11 +244,12 @@ async function convertParsedResponseToRaw( async function convertRawToParsedResponse< Body extends yup.Schema, - StatusCode extends yup.Schema, + StatusCode extends number, + T extends BodyType, Headers extends yup.Schema >( res: NextResponse, - schema: EndpointOutputSchema + schema: EndpointOutputSchema ): Promise>> { // TODO validate schema let contentType = res.headers.get("content-type"); From abecb7d2e09ddd433aaa9d06443f14be9157c44f Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Tue, 24 Dec 2024 09:48:04 -0800 Subject: [PATCH 36/40] updated pnpm lock --- pnpm-lock.yaml | 67 +++++++++----------------------------------------- 1 file changed, 12 insertions(+), 55 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15a2481a5..1a0b3d499 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -896,7 +896,7 @@ importers: version: 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-email: specifier: 2.1.0 - version: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@8.30.0) + version: 2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.13)(eslint@9.16.0(jiti@1.21.6)) yup: specifier: ^1.4.0 version: 1.4.0 @@ -2358,33 +2358,18 @@ packages: resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/core@1.6.2': - resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==} - '@floating-ui/core@1.6.8': resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} '@floating-ui/dom@1.6.12': resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} - '@floating-ui/dom@1.6.5': - resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==} - - '@floating-ui/react-dom@2.1.0': - resolution: {integrity: sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.2': - resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} - '@floating-ui/utils@0.2.8': resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} @@ -12422,10 +12407,6 @@ snapshots: dependencies: levn: 0.4.1 - '@floating-ui/core@1.6.2': - dependencies: - '@floating-ui/utils': 0.2.2 - '@floating-ui/core@1.6.8': dependencies: '@floating-ui/utils': 0.2.8 @@ -12435,24 +12416,17 @@ snapshots: '@floating-ui/core': 1.6.8 '@floating-ui/utils': 0.2.8 - '@floating-ui/dom@1.6.5': - dependencies: - '@floating-ui/core': 1.6.2 - '@floating-ui/utils': 0.2.2 - - '@floating-ui/react-dom@2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@floating-ui/dom': 1.6.5 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - '@floating-ui/react-dom@2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/dom': 1.6.12 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@floating-ui/utils@0.2.2': {} + '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.6.12 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) '@floating-ui/utils@0.2.8': {} @@ -14113,7 +14087,7 @@ snapshots: '@radix-ui/react-popper@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 - '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.12)(react@18.3.1) @@ -15386,23 +15360,6 @@ snapshots: '@swc/core-win32-x64-msvc@1.3.101': optional: true - '@swc/core@1.3.101': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.8 - optionalDependencies: - '@swc/core-darwin-arm64': 1.3.101 - '@swc/core-darwin-x64': 1.3.101 - '@swc/core-linux-arm-gnueabihf': 1.3.101 - '@swc/core-linux-arm64-gnu': 1.3.101 - '@swc/core-linux-arm64-musl': 1.3.101 - '@swc/core-linux-x64-gnu': 1.3.101 - '@swc/core-linux-x64-musl': 1.3.101 - '@swc/core-win32-arm64-msvc': 1.3.101 - '@swc/core-win32-ia32-msvc': 1.3.101 - '@swc/core-win32-x64-msvc': 1.3.101 - optional: true - '@swc/core@1.3.101(@swc/helpers@0.5.13)': dependencies: '@swc/counter': 0.1.3 @@ -17633,7 +17590,7 @@ snapshots: dependencies: eslint: 9.16.0(jiti@1.21.6) - eslint-config-turbo@1.10.12(eslint@8.30.0): + eslint-config-turbo@1.10.12(eslint@9.16.0(jiti@1.21.6)): dependencies: eslint: 9.16.0(jiti@1.21.6) eslint-plugin-turbo: 1.10.12(eslint@9.16.0(jiti@1.21.6)) @@ -18347,7 +18304,7 @@ snapshots: glob@10.3.4: dependencies: - foreground-child: 3.2.1 + foreground-child: 3.3.0 jackspeak: 2.3.6 minimatch: 9.0.5 minipass: 7.1.2 @@ -22102,7 +22059,7 @@ snapshots: terser: 5.31.1 webpack: 5.92.0(@swc/core@1.3.101)(esbuild@0.24.0) optionalDependencies: - '@swc/core': 1.3.101 + '@swc/core': 1.3.101(@swc/helpers@0.5.13) esbuild: 0.24.0 terser@5.31.1: @@ -22314,7 +22271,7 @@ snapshots: tinyglobby: 0.2.10 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.3.101 + '@swc/core': 1.3.101(@swc/helpers@0.5.13) postcss: 8.4.47 typescript: 5.3.3 transitivePeerDependencies: @@ -22860,7 +22817,7 @@ snapshots: acorn-import-attributes: 1.9.5(acorn@8.14.0) browserslist: 4.24.2 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.0 + enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 eslint-scope: 5.1.1 events: 3.3.0 From 66cb8f7ab15d658b780fa6ba8b253cf2e4f9c39a Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 27 Jan 2025 17:50:07 -0800 Subject: [PATCH 37/40] fixed response type --- apps/backend/scripts/test.ts | 105 +- apps/backend/src/app/api/v2/imports.ts | 210 +- apps/backend/src/app/api/v2/schema.ts | 3101 +++++++++-------- .../src/route-handlers/migration-handler.tsx | 78 +- 4 files changed, 1787 insertions(+), 1707 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index dad2082f5..61753c45a 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,4 +1,4 @@ -import { EndpointTransforms, EndpointsSchema, RawEndpointsHandlers, TransformFn, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; +import { EndpointTransforms, EndpointsSchema, ParsedResponseFromSchema, RawEndpointsHandlers, TransformFn, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; @@ -7,18 +7,18 @@ const schema1 = { 'POST': { 'default': { input: { - query: yupObject({}), + query: yupObject({}).defined(), body: yupObject({ fullName: yupString().defined(), - }), + }).defined(), }, output: { - statusCode: yupNumber(), - bodyType: yupString().oneOf(['json']), + statusCode: yupNumber().defined(), + bodyType: yupString().oneOf(['json']).defined(), body: yupObject({ id: yupString().defined(), fullName: yupString().defined(), - }), + }).defined(), }, }, }, @@ -27,13 +27,13 @@ const schema1 = { 'POST': { 'default': { input: { - query: yupObject({}), + query: yupObject({}).defined(), body: yupObject({}), }, output: { - statusCode: yupNumber(), - bodyType: yupString().oneOf(['json']), - body: yupObject({}), + statusCode: yupNumber().defined(), + bodyType: yupString().oneOf(['json']).defined(), + body: yupObject({}).defined(), }, }, }, @@ -44,17 +44,17 @@ const schema1 = { input: { query: yupObject({ same: yupString().defined(), - }), + }).defined(), body: yupObject({ same: yupString().defined(), - }), + }).defined(), }, output: { - statusCode: yupNumber(), + statusCode: yupNumber().defined(), bodyType: yupString().oneOf(['json']), body: yupObject({ same: yupString().defined(), - }), + }).defined(), }, }, }, @@ -67,31 +67,31 @@ const schema2 = { 'POST': { 'default': { input: { - query: yupObject({}), + query: yupObject({}).defined(), body: yupObject({ firstName: yupString().defined(), lastName: yupString().defined(), - }), + }).defined(), }, output: { - statusCode: yupNumber(), - bodyType: yupString().oneOf(['json']), + statusCode: yupNumber().defined(), + bodyType: yupString().oneOf(['json']).defined(), body: yupObject({ id: yupString().defined(), - }), + }).defined(), }, }, }, 'GET': { 'default': { input: { - query: yupObject({}), - body: yupObject({}), + query: yupObject({}).defined(), + body: yupObject({}).defined(), }, output: { - statusCode: yupNumber(), - bodyType: yupString().oneOf(['json']), - body: yupObject({}), + statusCode: yupNumber().defined(), + bodyType: yupString().oneOf(['json']).defined(), + body: yupObject({}).defined(), }, }, }, @@ -102,17 +102,17 @@ const schema2 = { input: { query: yupObject({ same: yupString().defined(), - }), + }).defined(), body: yupObject({ same: yupString().defined(), - }), + }).defined(), }, output: { - statusCode: yupNumber(), - bodyType: yupString().oneOf(['json']), + statusCode: yupNumber().defined(), + bodyType: yupString().oneOf(['json']).defined(), body: yupObject({ same: yupString().defined(), - }), + }).defined(), }, }, }, @@ -151,6 +151,7 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp id: (result.body as any).id, fullName: req.body.fullName, }, + bodyType: 'json', }; }, }, @@ -161,7 +162,24 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp return { statusCode: 200, headers: {}, - body: {}, + body: { + test: 'example-token', + }, + bodyType: 'json', + }; + }, + }, + }, + '/same': { + 'POST': { + 'default': async ({ req, newEndpointHandlers }) => { + return { + statusCode: 200, + headers: {}, + body: { + same: req.body.same, + }, + bodyType: 'json', }; }, }, @@ -174,20 +192,35 @@ const y = null as unknown as x; const z = null as unknown as x['/users']['POST']['default']; -type a = TransformFn; +type a = Awaited>>; + +type b = ParsedResponseFromSchema + +const c = null as unknown as b; + +// endpointHandlers1['/tokens']['POST']['default']({ +// url: 'http://localhost:3000/tokens', +// method: 'POST', +// headers: {}, +// body: {}, +// query: {}, +// }).then((res) => { +// console.log(res); +// }).catch((err) => { +// console.error(err); +// }); endpointHandlers1['/users']['POST']['default']({ - url: 'http://localhost:3000/users', - method: 'POST', - headers: {}, body: { - fullName: 'Full Name', + fullName: 'John Doe', }, query: {}, + headers: {}, + method: 'POST', + url: 'http://localhost:3000/users', }).then((res) => { console.log(res); }).catch((err) => { console.error(err); }); - diff --git a/apps/backend/src/app/api/v2/imports.ts b/apps/backend/src/app/api/v2/imports.ts index fa13b169b..296cb747a 100644 --- a/apps/backend/src/app/api/v2/imports.ts +++ b/apps/backend/src/app/api/v2/imports.ts @@ -1,9 +1,9 @@ import { GET as iGet } from '../../api/v1/route'; import { GET as iUsersGet } from '../../api/v1/users/route'; import { POST as iUsersPost } from '../../api/v1/users/route'; +import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; import { GET as iTeamsGet } from '../../api/v1/teams/route'; import { POST as iTeamsPost } from '../../api/v1/teams/route'; -import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; import { GET as iTeamPermissionDefinitionsGet } from '../../api/v1/team-permission-definitions/route'; @@ -11,8 +11,8 @@ import { POST as iTeamPermissionDefinitionsPost } from '../../api/v1/team-permis import { GET as iEmailTemplatesGet } from '../../api/v1/email-templates/route'; import { GET as iContactChannelsGet } from '../../api/v1/contact-channels/route'; import { POST as iContactChannelsPost } from '../../api/v1/contact-channels/route'; -import { POST as iCheckVersionPost } from '../../api/v1/check-version/route'; import { POST as iCheckFeatureSupportPost } from '../../api/v1/check-feature-support/route'; +import { POST as iCheckVersionPost } from '../../api/v1/check-version/route'; import { GET as iUsersMeGet } from '../../api/v1/users/me/route'; import { DELETE as iUsersMeDelete } from '../../api/v1/users/me/route'; import { PATCH as iUsersMePatch } from '../../api/v1/users/me/route'; @@ -25,73 +25,78 @@ import { DELETE as iTeamsTeamIdDelete } from '../../api/v1/teams/[team_id]/route import { PATCH as iTeamsTeamIdPatch } from '../../api/v1/teams/[team_id]/route'; import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; +import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; import { GET as iProjectsCurrentGet } from '../../api/v1/projects/current/route'; import { DELETE as iProjectsCurrentDelete } from '../../api/v1/projects/current/route'; import { PATCH as iProjectsCurrentPatch } from '../../api/v1/projects/current/route'; -import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; -import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { POST as iInternalSendTestEmailPost } from '../../api/v1/internal/send-test-email/route'; +import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; +import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; +import { GET as iInternalMetricsGet } from '../../api/v1/internal/metrics/route'; import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; +import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; +import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; -import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; -import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { POST as iContactChannelsVerifyPost } from '../../api/v1/contact-channels/verify/route'; -import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; -import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; -import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; import { POST as iAuthSessionsPost } from '../../api/v1/auth/sessions/route'; -import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; -import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; import { POST as iTeamMembershipsTeamIdUserIdPost } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { DELETE as iTeamMembershipsTeamIdUserIdDelete } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; +import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; +import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iTeamInvitationsAcceptDetailsPost } from '../../api/v1/team-invitations/accept/details/route'; import { POST as iTeamInvitationsAcceptCheckCodePost } from '../../api/v1/team-invitations/accept/check-code/route'; -import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; -import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; -import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; -import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; -import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; -import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; -import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; import { GET as iContactChannelsUserIdContactChannelIdGet } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { DELETE as iContactChannelsUserIdContactChannelIdDelete } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { PATCH as iContactChannelsUserIdContactChannelIdPatch } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; -import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; +import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; +import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; +import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; import { POST as iAuthPasswordSignUpPost } from '../../api/v1/auth/password/sign-up/route'; import { POST as iAuthPasswordSignInPost } from '../../api/v1/auth/password/sign-in/route'; import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; -import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; +import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; -import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; +import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; +import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; -import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; -import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; -import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; -import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; -import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; +import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; +import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; +import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; +import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; +import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; +import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; import { POST as iTeamPermissionsTeamIdUserIdPermissionIdPost } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; import { DELETE as iTeamPermissionsTeamIdUserIdPermissionIdDelete } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; +import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; +import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; +import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; +import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; +import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; +import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; +import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; +import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; +import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; +import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; import { DELETE as iIntegrationsNeonOauthProvidersOauthProviderIdDelete } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; import { PATCH as iIntegrationsNeonOauthProvidersOauthProviderIdPatch } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; -import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; import { POST as iIntegrationsNeonInternalConfirmPost } from '../../api/v1/integrations/neon/internal/confirm/route'; -import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; +import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; +import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; import { GET as iIntegrationsNeonApiKeysApiKeyIdGet } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; import { PATCH as iIntegrationsNeonApiKeysApiKeyIdPatch } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; -import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; -import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; -import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; -import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; -import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; -import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; -import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; -import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; +import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; import { GET as iIntegrationsNeonOauthIdpRouteGet } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { POST as iIntegrationsNeonOauthIdpRoutePost } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { PUT as iIntegrationsNeonOauthIdpRoutePut } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; @@ -99,15 +104,12 @@ import { DELETE as iIntegrationsNeonOauthIdpRouteDelete } from '../../api/v1/int import { PATCH as iIntegrationsNeonOauthIdpRoutePatch } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { OPTIONS as iIntegrationsNeonOauthIdpRouteOptions } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { HEAD as iIntegrationsNeonOauthIdpRouteHead } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; -import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; -import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; -import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; export const endpoints = { '/': { GET: iGet }, '/users': { GET: iUsersGet, POST: iUsersPost }, - '/teams': { GET: iTeamsGet, POST: iTeamsPost }, '/team-permissions': { GET: iTeamPermissionsGet }, + '/teams': { GET: iTeamsGet, POST: iTeamsPost }, '/team-member-profiles': { GET: iTeamMemberProfilesGet }, '/team-invitations': { GET: iTeamInvitationsGet }, '/team-permission-definitions': { @@ -116,8 +118,8 @@ export const endpoints = { }, '/email-templates': { GET: iEmailTemplatesGet }, '/contact-channels': { GET: iContactChannelsGet, POST: iContactChannelsPost }, - '/check-version': { POST: iCheckVersionPost }, '/check-feature-support': { POST: iCheckFeatureSupportPost }, + '/check-version': { POST: iCheckVersionPost }, '/users/me': { GET: iUsersMeGet, DELETE: iUsersMeDelete, @@ -136,58 +138,50 @@ export const endpoints = { }, '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, + '/team-permission-definitions/[permission_id]': { + DELETE: iTeamPermissionDefinitionsPermissionIdDelete, + PATCH: iTeamPermissionDefinitionsPermissionIdPatch, + }, '/projects/current': { GET: iProjectsCurrentGet, DELETE: iProjectsCurrentDelete, PATCH: iProjectsCurrentPatch, }, - '/team-permission-definitions/[permission_id]': { - DELETE: iTeamPermissionDefinitionsPermissionIdDelete, - PATCH: iTeamPermissionDefinitionsPermissionIdPatch, + '/internal/send-test-email': { POST: iInternalSendTestEmailPost }, + '/internal/projects': { + GET: iInternalProjectsGet, + POST: iInternalProjectsPost, }, + '/internal/metrics': { GET: iInternalMetricsGet }, '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, + '/internal/api-keys': { + GET: iInternalApiKeysGet, + POST: iInternalApiKeysPost, + }, '/email-templates/[type]': { GET: iEmailTemplatesTypeGet, DELETE: iEmailTemplatesTypeDelete, PATCH: iEmailTemplatesTypePatch, }, - '/internal/api-keys': { - GET: iInternalApiKeysGet, - POST: iInternalApiKeysPost, - }, '/contact-channels/verify': { POST: iContactChannelsVerifyPost }, + '/auth/sessions': { POST: iAuthSessionsPost }, '/contact-channels/send-verification-code': { POST: iContactChannelsSendVerificationCodePost, }, - '/internal/projects': { - GET: iInternalProjectsGet, - POST: iInternalProjectsPost, + '/team-memberships/[team_id]/[user_id]': { + POST: iTeamMembershipsTeamIdUserIdPost, + DELETE: iTeamMembershipsTeamIdUserIdDelete, }, - '/auth/sessions': { POST: iAuthSessionsPost }, '/team-member-profiles/[team_id]/[user_id]': { GET: iTeamMemberProfilesTeamIdUserIdGet, PATCH: iTeamMemberProfilesTeamIdUserIdPatch, }, - '/team-memberships/[team_id]/[user_id]': { - POST: iTeamMembershipsTeamIdUserIdPost, - DELETE: iTeamMembershipsTeamIdUserIdDelete, - }, '/team-invitations/accept/details': { POST: iTeamInvitationsAcceptDetailsPost, }, '/team-invitations/accept/check-code': { POST: iTeamInvitationsAcceptCheckCodePost, }, - '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, - '/integrations/neon/oauth-providers': { - GET: iIntegrationsNeonOauthProvidersGet, - POST: iIntegrationsNeonOauthProvidersPost, - }, - '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, - '/integrations/neon/api-keys': { - GET: iIntegrationsNeonApiKeysGet, - POST: iIntegrationsNeonApiKeysPost, - }, '/internal/api-keys/[api_key_id]': { GET: iInternalApiKeysApiKeyIdGet, PATCH: iInternalApiKeysApiKeyIdPatch, @@ -195,67 +189,86 @@ export const endpoints = { '/contact-channels/verify/check-code': { POST: iContactChannelsVerifyCheckCodePost, }, - '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, '/contact-channels/[user_id]/[contact_channel_id]': { GET: iContactChannelsUserIdContactChannelIdGet, DELETE: iContactChannelsUserIdContactChannelIdDelete, PATCH: iContactChannelsUserIdContactChannelIdPatch, }, - '/auth/password/update': { POST: iAuthPasswordUpdatePost }, + '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, + '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, + '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, '/auth/password/sign-up': { POST: iAuthPasswordSignUpPost }, '/auth/password/sign-in': { POST: iAuthPasswordSignInPost }, '/auth/password/set': { POST: iAuthPasswordSetPost }, - '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, + '/auth/password/update': { POST: iAuthPasswordUpdatePost }, '/auth/password/reset': { POST: iAuthPasswordResetPost }, - '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, + '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, + '/auth/oauth/token': { POST: iAuthOauthTokenPost }, '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, + '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, + '/auth/passkey/initiate-passkey-registration': { + POST: iAuthPasskeyInitiatePasskeyRegistrationPost, + }, '/auth/passkey/initiate-passkey-authentication': { POST: iAuthPasskeyInitiatePasskeyAuthenticationPost, }, - '/auth/passkey/initiate-passkey-registration': { - POST: iAuthPasskeyInitiatePasskeyRegistrationPost, + '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, + '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, + '/integrations/neon/oauth-providers': { + GET: iIntegrationsNeonOauthProvidersGet, + POST: iIntegrationsNeonOauthProvidersPost, }, - '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, - '/auth/oauth/token': { POST: iAuthOauthTokenPost }, - '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, - '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, '/team-permissions/[team_id]/[user_id]/[permission_id]': { POST: iTeamPermissionsTeamIdUserIdPermissionIdPost, DELETE: iTeamPermissionsTeamIdUserIdPermissionIdDelete, }, - '/integrations/neon/oauth-providers/[oauth_provider_id]': { - DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, - PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, + '/connected-accounts/[user_id]/[provider_id]/access-token': { + POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, + }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, + }, + '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, + '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, + '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, + '/auth/oauth/callback/[provider_id]': { + GET: iAuthOauthCallbackProviderIdGet, + POST: iAuthOauthCallbackProviderIdPost, + }, + '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, + '/integrations/neon/api-keys': { + GET: iIntegrationsNeonApiKeysGet, + POST: iIntegrationsNeonApiKeysPost, + }, + '/auth/oauth/authorize/[provider_id]': { + GET: iAuthOauthAuthorizeProviderIdGet, }, '/integrations/neon/projects/provision': { POST: iIntegrationsNeonProjectsProvisionPost, }, + '/integrations/neon/oauth-providers/[oauth_provider_id]': { + DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, + PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, + }, '/integrations/neon/internal/confirm': { POST: iIntegrationsNeonInternalConfirmPost, }, + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, + }, + '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, '/integrations/neon/oauth/authorize': { GET: iIntegrationsNeonOauthAuthorizeGet, }, - '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, '/integrations/neon/api-keys/[api_key_id]': { GET: iIntegrationsNeonApiKeysApiKeyIdGet, PATCH: iIntegrationsNeonApiKeysApiKeyIdPatch, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { - POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, - }, - '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { - POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, - }, - '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, - '/auth/oauth/authorize/[provider_id]': { - GET: iAuthOauthAuthorizeProviderIdGet, + '/integrations/neon/projects/transfer/initiate': { + POST: iIntegrationsNeonProjectsTransferInitiatePost, }, - '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, - '/auth/oauth/callback/[provider_id]': { - GET: iAuthOauthCallbackProviderIdGet, - POST: iAuthOauthCallbackProviderIdPost, + '/integrations/neon/projects/transfer/confirm': { + POST: iIntegrationsNeonProjectsTransferConfirmPost, }, '/integrations/neon/oauth/idp/[[...route]]': { GET: iIntegrationsNeonOauthIdpRouteGet, @@ -266,13 +279,4 @@ export const endpoints = { OPTIONS: iIntegrationsNeonOauthIdpRouteOptions, HEAD: iIntegrationsNeonOauthIdpRouteHead, }, - '/integrations/neon/projects/transfer/confirm': { - POST: iIntegrationsNeonProjectsTransferConfirmPost, - }, - '/integrations/neon/projects/transfer/initiate': { - POST: iIntegrationsNeonProjectsTransferInitiatePost, - }, - '/auth/oauth/connected-accounts/[provider_id]/access-token': { - POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, - }, }; diff --git a/apps/backend/src/app/api/v2/schema.ts b/apps/backend/src/app/api/v2/schema.ts index c4d8c9628..f7f693fcf 100644 --- a/apps/backend/src/app/api/v2/schema.ts +++ b/apps/backend/src/app/api/v2/schema.ts @@ -298,6 +298,109 @@ export const endpointSchema = { }, }, }, + '/team-permissions': { + GET: { + client: { + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + server: { + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: { + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + user_id: yupString().defined(), + team_id: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, '/teams': { GET: { client: { @@ -484,109 +587,6 @@ export const endpointSchema = { }, }, }, - '/team-permissions': { - GET: { - client: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - server: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - admin: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - }, '/team-member-profiles': { GET: { client: { @@ -1143,28 +1143,28 @@ export const endpointSchema = { }, }, }, - '/check-version': { + '/check-feature-support': { POST: { default: { - input: { - body: yupObject({ clientVersion: yupString().defined() }).optional(), - }, + input: {}, output: { statusCode: [200], - bodyType: 'json', - body: yupMixed().defined(), + bodyType: 'text', + body: yupString().defined(), }, }, }, }, - '/check-feature-support': { + '/check-version': { POST: { default: { - input: {}, + input: { + body: yupObject({ clientVersion: yupString().defined() }).optional(), + }, output: { statusCode: [200], - bodyType: 'text', - body: yupString().defined(), + bodyType: 'json', + body: yupMixed().defined(), }, }, }, @@ -1206,7 +1206,7 @@ export const endpointSchema = { requires_totp_mfa: yupBoolean().defined(), otp_auth_enabled: yupBoolean().defined(), passkey_auth_enabled: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, server: { @@ -1249,7 +1249,7 @@ export const endpointSchema = { ).defined(), auth_with_email: yupBoolean().defined(), requires_totp_mfa: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, admin: { @@ -1292,7 +1292,7 @@ export const endpointSchema = { ).defined(), auth_with_email: yupBoolean().defined(), requires_totp_mfa: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, }, @@ -1371,7 +1371,7 @@ export const endpointSchema = { requires_totp_mfa: yupBoolean().defined(), otp_auth_enabled: yupBoolean().defined(), passkey_auth_enabled: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, server: { @@ -1431,7 +1431,7 @@ export const endpointSchema = { ).defined(), auth_with_email: yupBoolean().defined(), requires_totp_mfa: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, admin: { @@ -1491,7 +1491,7 @@ export const endpointSchema = { ).defined(), auth_with_email: yupBoolean().defined(), requires_totp_mfa: yupBoolean().defined(), - }).nullable(), + }).defined(), }, }, }, @@ -2072,18 +2072,61 @@ export const endpointSchema = { }, }, }, - '/projects/current': { - GET: { - client: { - input: {}, + '/team-permission-definitions/[permission_id]': { + DELETE: { + admin: { + input: { + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, output: { statusCode: [200], - bodyType: 'json', + bodyType: 'success', headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { body: yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - config: yupObject({ + id: yupString().optional(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), + }).defined(), + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, + '/projects/current': { + GET: { + client: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + config: yupObject({ sign_up_enabled: yupBoolean().defined(), credential_enabled: yupBoolean().defined(), magic_link_enabled: yupBoolean().defined(), @@ -2416,401 +2459,145 @@ export const endpointSchema = { }, }, }, - '/team-permission-definitions/[permission_id]': { - DELETE: { - admin: { - input: { - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - PATCH: { + '/internal/send-test-email': { + POST: { admin: { input: { body: yupObject({ - id: yupString().optional(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).optional(), + recipient_email: yupString().defined(), + email_config: yupObject({ + host: yupString().defined(), + port: yupNumber().defined(), + username: yupString().defined(), + password: yupString().defined(), + sender_name: yupString().defined(), + sender_email: yupString().defined(), + }).defined(), }).defined(), - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), + success: yupBoolean().defined(), + error_message: yupString().optional(), }).defined(), }, }, }, }, - '/team-invitations/[id]': { - DELETE: { - client: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - server: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - admin: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - }, - '/email-templates/[type]': { + '/internal/projects': { GET: { - admin: { + client: { input: { - params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - }).optional(), + params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), + items: yupArray( + yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + description: yupString().defined(), + created_at_millis: yupNumber().defined(), + user_count: yupNumber().defined(), + is_production_mode: yupBoolean().defined(), + config: yupObject({ + id: yupString().defined(), + allow_localhost: yupBoolean().defined(), + sign_up_enabled: yupBoolean().defined(), + credential_enabled: yupBoolean().defined(), + magic_link_enabled: yupBoolean().defined(), + passkey_enabled: yupBoolean().defined(), + legacy_global_jwt_signing: yupBoolean().defined(), + client_team_creation_enabled: yupBoolean().defined(), + client_user_deletion_enabled: yupBoolean().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + enabled: yupBoolean().defined(), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).defined(), + ).defined(), + enabled_oauth_providers: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).defined(), + ).defined(), + domains: yupArray( + yupObject({ + domain: yupString().defined(), + handler_path: yupString().defined(), + }).defined(), + ).defined(), + email_config: yupObject({ + type: yupString().defined().oneOf(['shared', 'standard']), + host: yupString().optional(), + port: yupNumber().optional(), + username: yupString().optional(), + password: yupString().optional(), + sender_name: yupString().optional(), + sender_email: yupString().optional(), + }).defined(), + create_team_on_sign_up: yupBoolean().defined(), + team_creator_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + team_member_default_permissions: yupArray( + yupObject({ id: yupString().defined() }).defined(), + ).defined(), + }).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), }).defined(), }, }, - }, - DELETE: { - admin: { + server: { input: { - params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - }).optional(), + params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - PATCH: { - admin: { - input: { - body: yupObject({ - content: yupMixed().optional(), - subject: yupString().optional(), - }).defined(), - params: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - type: yupString() - .defined() - .oneOf([ - 'email_verification', - 'password_reset', - 'magic_link', - 'team_invitation', - ]), - subject: yupString().defined(), - content: yupMixed().nullable(), - is_default: yupBoolean().defined(), - }).defined(), - }, - }, - }, - }, - '/internal/api-keys': { - GET: { - admin: { - input: { - params: yupObject({ api_key_id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - POST: { - admin: { - input: { - body: yupObject({ - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - has_publishable_client_key: yupBoolean().defined(), - has_secret_server_key: yupBoolean().defined(), - has_super_secret_admin_key: yupBoolean().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).defined(), - }, - }, - }, - }, - '/contact-channels/verify': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/contact-channels/send-verification-code': { - POST: { - client: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/internal/projects': { - GET: { - client: { - input: { - params: yupObject({ projectId: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - description: yupString().defined(), - created_at_millis: yupNumber().defined(), - user_count: yupNumber().defined(), - is_production_mode: yupBoolean().defined(), - config: yupObject({ - id: yupString().defined(), - allow_localhost: yupBoolean().defined(), - sign_up_enabled: yupBoolean().defined(), - credential_enabled: yupBoolean().defined(), - magic_link_enabled: yupBoolean().defined(), - passkey_enabled: yupBoolean().defined(), - legacy_global_jwt_signing: yupBoolean().defined(), - client_team_creation_enabled: yupBoolean().defined(), - client_user_deletion_enabled: yupBoolean().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - enabled: yupBoolean().defined(), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).defined(), - ).defined(), - enabled_oauth_providers: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).defined(), - ).defined(), - domains: yupArray( - yupObject({ - domain: yupString().defined(), - handler_path: yupString().defined(), - }).defined(), - ).defined(), - email_config: yupObject({ - type: yupString().defined().oneOf(['shared', 'standard']), - host: yupString().optional(), - port: yupNumber().optional(), - username: yupString().optional(), - password: yupString().optional(), - sender_name: yupString().optional(), - sender_email: yupString().optional(), - }).defined(), - create_team_on_sign_up: yupBoolean().defined(), - team_creator_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - team_member_default_permissions: yupArray( - yupObject({ id: yupString().defined() }).defined(), - ).defined(), - }).defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - server: { - input: { - params: yupObject({ projectId: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', + bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ items: yupArray( @@ -3485,15 +3272,254 @@ export const endpointSchema = { }, }, }, - '/auth/sessions': { - POST: { - server: { - input: { - body: yupObject({ - user_id: yupString().defined(), - expires_in_millis: yupNumber().optional(), - }).defined(), - }, + '/internal/metrics': { + GET: { + admin: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + total_users: yupNumber().defined(), + daily_users: yupArray( + yupObject({ + date: yupString().defined(), + activity: yupNumber().defined(), + }).defined(), + ).defined(), + daily_active_users: yupArray( + yupObject({ + date: yupString().defined(), + activity: yupNumber().defined(), + }).defined(), + ).defined(), + users_by_country: yupMixed().defined(), + recently_registered: yupMixed().defined(), + recently_active: yupMixed().defined(), + login_methods: yupMixed().defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/[id]': { + DELETE: { + client: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: { + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, + '/internal/api-keys': { + GET: { + admin: { + input: { + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).defined(), + }, + }, + }, + }, + '/email-templates/[type]': { + GET: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + admin: { + input: { + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + content: yupMixed().optional(), + subject: yupString().optional(), + }).defined(), + params: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + type: yupString() + .defined() + .oneOf([ + 'email_verification', + 'password_reset', + 'magic_link', + 'team_invitation', + ]), + subject: yupString().defined(), + content: yupMixed().nullable(), + is_default: yupBoolean().defined(), + }).defined(), + }, + }, + }, + }, + '/contact-channels/verify': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/sessions': { + POST: { + server: { + input: { + body: yupObject({ + user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), + }).defined(), + }, output: { statusCode: [200], bodyType: 'json', @@ -3521,6 +3547,119 @@ export const endpointSchema = { }, }, }, + '/contact-channels/send-verification-code': { + POST: { + client: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/team-memberships/[team_id]/[user_id]': { + POST: { + server: { + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + admin: { + input: { + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + admin: { + input: { + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + }, '/team-member-profiles/[team_id]/[user_id]': { GET: { client: { @@ -3761,11 +3900,178 @@ export const endpointSchema = { }).defined(), query: yupObject({ user_id: yupString().optional(), - team_id: yupString().optional(), + team_id: yupString().optional(), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user: yupObject({ + id: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + team_id: yupString().defined(), + user_id: yupString().defined(), + display_name: yupString().nullable(), + profile_image_url: yupString().nullable(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/details': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + team_id: yupString().defined(), + team_display_name: yupString().defined(), + }).defined(), + }, + }, + }, + }, + '/team-invitations/accept/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/internal/api-keys/[api_key_id]': { + GET: { + admin: { + input: { + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + PATCH: { + admin: { + input: { + body: yupObject({ + description: yupString().optional(), + revoked: yupBoolean().optional().oneOf([true]), + }).defined(), + params: yupObject({ api_key_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + }, + }, + }, + }, + '/contact-channels/verify/check-code': { + POST: { + default: { + input: { body: yupObject({ code: yupString().defined() }).defined() }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/contact-channels/[user_id]/[contact_channel_id]': { + GET: { + client: { + input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), }).optional(), params: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { @@ -3773,83 +4079,65 @@ export const endpointSchema = { bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - user: yupObject({ - id: yupString().defined(), - primary_email: yupString().nullable(), - primary_email_verified: yupBoolean().defined(), - primary_email_auth_enabled: yupBoolean().defined(), - display_name: yupString().nullable(), - selected_team: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).nullable(), - selected_team_id: yupString().nullable(), - profile_image_url: yupString().nullable(), - signed_up_at_millis: yupNumber().defined(), - has_password: yupBoolean().defined(), - otp_auth_enabled: yupBoolean().defined(), - passkey_auth_enabled: yupBoolean().defined(), - client_metadata: yupMixed().nullable(), - client_read_only_metadata: yupMixed().nullable(), - server_metadata: yupMixed().nullable(), - last_active_at_millis: yupNumber().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString().defined(), - account_id: yupString().defined(), - email: yupString().optional().nullable(), - }).defined(), - ).defined(), - auth_with_email: yupBoolean().defined(), - requires_totp_mfa: yupBoolean().defined(), - }).defined(), - team_id: yupString().defined(), user_id: yupString().defined(), - display_name: yupString().nullable(), - profile_image_url: yupString().nullable(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, - }, - }, - '/team-memberships/[team_id]/[user_id]': { - POST: { server: { input: { - params: yupObject({ - team_id: yupString().optional(), + query: yupObject({ user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, admin: { input: { - params: yupObject({ - team_id: yupString().optional(), + query: yupObject({ user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, @@ -3857,9 +4145,13 @@ export const endpointSchema = { DELETE: { client: { input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), params: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { @@ -3871,9 +4163,13 @@ export const endpointSchema = { }, server: { input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), params: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { @@ -3885,9 +4181,13 @@ export const endpointSchema = { }, admin: { input: { + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), params: yupObject({ - team_id: yupString().defined(), user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).optional(), }, output: { @@ -3898,919 +4198,910 @@ export const endpointSchema = { }, }, }, - }, - '/team-invitations/accept/details': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + PATCH: { + client: { + input: { + body: yupObject({ + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { statusCode: [200], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - team_id: yupString().defined(), - team_display_name: yupString().defined(), + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, - }, - }, - '/team-invitations/accept/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + server: { + input: { + body: yupObject({ + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, output: { statusCode: [200], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), }).defined(), }, }, - }, - }, - '/integrations/neon/webhooks': { - POST: { - default: { + admin: { input: { body: yupObject({ - url: yupString().defined(), - description: yupString().optional(), - }).defined(), + is_verified: yupBoolean().optional(), + value: yupString().optional(), + type: yupString().optional().oneOf(['email']), + used_for_auth: yupBoolean().optional(), + is_primary: yupBoolean().optional(), + }).optional(), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ secret: yupString().defined() }).defined(), + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), }, }, }, }, - '/integrations/neon/oauth-providers': { - GET: { + '/auth/sessions/current': { + DELETE: { + client: { + input: {}, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: {}, + output: { statusCode: [200], bodyType: 'success' }, + }, admin: { + input: {}, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/otp/send-sign-in-code': { + POST: { + client: { input: { - params: yupObject({ - oauth_provider_id: yupString() - .optional() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).optional(), + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + server: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, - }, - POST: { admin: { input: { body: yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().optional().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), - params: yupObject({ - oauth_provider_id: yupString() - .optional() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).optional(), + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, }, }, - '/integrations/neon/oauth': { - GET: { + '/auth/otp/sign-in': { + POST: { default: { - input: {}, + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], - bodyType: 'text', - body: yupString().defined(), + bodyType: 'json', + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), }, }, }, }, - '/integrations/neon/api-keys': { - GET: { - admin: { + '/auth/password/sign-up': { + POST: { + client: { input: { - params: yupObject({ api_key_id: yupString().optional() }).optional(), + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), + }).defined(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), + }, + }, + server: { + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), + }).defined(), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), }).defined(), }, }, - }, - POST: { admin: { input: { body: yupObject({ - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - has_publishable_client_key: yupBoolean().defined(), - has_secret_server_key: yupBoolean().defined(), - has_super_secret_admin_key: yupBoolean().defined(), + email: yupString().defined(), + password: yupString().defined(), + verification_callback_url: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), }).defined(), }, }, }, }, - '/internal/api-keys/[api_key_id]': { - GET: { - admin: { + '/auth/password/sign-in': { + POST: { + client: { input: { - params: yupObject({ api_key_id: yupString().defined() }).optional(), + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + }).defined(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), }, }, - }, - PATCH: { - admin: { + server: { input: { body: yupObject({ - description: yupString().optional(), - revoked: yupBoolean().optional().oneOf([true]), + email: yupString().defined(), + password: yupString().defined(), }).defined(), - params: yupObject({ api_key_id: yupString().defined() }).optional(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), + }).defined(), }, }, - }, - }, - '/contact-channels/verify/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + admin: { + input: { + body: yupObject({ + email: yupString().defined(), + password: yupString().defined(), + }).defined(), + }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + access_token: yupString().defined(), + refresh_token: yupString().defined(), + user_id: yupString().defined(), }).defined(), }, }, }, }, - '/auth/sessions/current': { - DELETE: { + '/auth/password/set': { + POST: { client: { - input: {}, + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: {}, + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: {}, + input: { + body: yupObject({ password: yupString().defined() }).defined(), + }, output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/contact-channels/[user_id]/[contact_channel_id]': { - GET: { + '/auth/password/update': { + POST: { client: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + old_password: yupString().defined(), + new_password: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + old_password: yupString().defined(), + new_password: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + body: yupObject({ + old_password: yupString().defined(), + new_password: yupString().defined(), + }).defined(), }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/password/reset': { + POST: { + default: { + input: { body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + password: yupString().defined(), + code: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, }, - DELETE: { + }, + '/auth/password/send-reset-code': { + POST: { client: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, output: { statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), + bodyType: 'json', + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), }, }, server: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, output: { statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), + bodyType: 'json', + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), }, }, admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), }, output: { statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), + bodyType: 'json', + body: yupObject({ + success: yupString() + .defined() + .oneOf(['maybe, only if user with e-mail exists']), + }).defined(), }, }, }, - PATCH: { - client: { + }, + '/auth/oauth/token': { + POST: { + default: { input: { body: yupObject({ - value: yupString().optional(), - type: yupString().optional().oneOf(['email']), - used_for_auth: yupBoolean().optional(), - is_primary: yupBoolean().optional(), + grant_type: yupString() + .defined() + .oneOf(['authorization_code', 'refresh_token']), + }).defined(), + }, + output: { + statusCode: [], + bodyType: 'json', + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, + '/auth/mfa/sign-in': { + POST: { + default: { + input: { + body: yupObject({ + type: yupString().defined().oneOf(['totp']), + totp: yupString().defined(), + code: yupString().defined(), }).defined(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), }).defined(), }, }, - server: { - input: { - body: yupObject({ - is_verified: yupBoolean().optional(), - value: yupString().optional(), - type: yupString().optional().oneOf(['email']), - used_for_auth: yupBoolean().optional(), - is_primary: yupBoolean().optional(), - }).optional(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + }, + }, + '/auth/passkey/register': { + POST: { + default: { + input: { + body: yupObject({ + credential: yupMixed().defined(), + code: yupString().defined(), + }).defined(), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), + body: yupObject({ user_handle: yupString().defined() }).optional(), }, }, - admin: { - input: { + }, + }, + '/auth/passkey/initiate-passkey-registration': { + POST: { + client: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - is_verified: yupBoolean().optional(), - value: yupString().optional(), - type: yupString().optional().oneOf(['email']), - used_for_auth: yupBoolean().optional(), - is_primary: yupBoolean().optional(), - }).optional(), - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), + options_json: yupMixed().defined(), + code: yupString().defined(), }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), + }, + }, + server: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + options_json: yupMixed().defined(), + code: yupString().defined(), }).optional(), }, + }, + admin: { + input: {}, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), + }).optional(), }, }, }, }, - '/auth/password/update': { + '/auth/passkey/initiate-passkey-authentication': { POST: { client: { - input: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: { + input: {}, + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), + options_json: yupMixed().defined(), + code: yupString().defined(), }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/auth/password/sign-up': { + '/auth/passkey/sign-in': { POST: { - client: { + default: { input: { body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - verification_callback_url: yupString().defined(), + authentication_response: yupMixed().defined(), + code: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - access_token: yupString().defined(), refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), user_id: yupString().defined(), }).defined(), }, }, - server: { + }, + }, + '/integrations/neon/webhooks': { + POST: { + default: { input: { body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - verification_callback_url: yupString().defined(), + url: yupString().defined(), + description: yupString().optional(), }).defined(), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ - access_token: yupString().defined(), - refresh_token: yupString().defined(), - user_id: yupString().defined(), - }).defined(), + body: yupObject({ secret: yupString().defined() }).defined(), }, }, + }, + }, + '/integrations/neon/oauth-providers': { + GET: { admin: { input: { - body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - verification_callback_url: yupString().defined(), - }).defined(), + params: yupObject({ + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), }, output: { statusCode: [200], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - access_token: yupString().defined(), - refresh_token: yupString().defined(), - user_id: yupString().defined(), + items: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), }).defined(), }, }, }, - }, - '/auth/password/sign-in': { POST: { - client: { + admin: { input: { body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - }).defined(), + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().optional().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + params: yupObject({ + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - access_token: yupString().defined(), - refresh_token: yupString().defined(), - user_id: yupString().defined(), - }).defined(), + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), }, }, + }, + }, + '/team-permissions/[team_id]/[user_id]/[permission_id]': { + POST: { server: { input: { - body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - }).defined(), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - access_token: yupString().defined(), - refresh_token: yupString().defined(), + id: yupString().defined(), user_id: yupString().defined(), + team_id: yupString().defined(), }).defined(), }, }, admin: { input: { - body: yupObject({ - email: yupString().defined(), - password: yupString().defined(), - }).defined(), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', + headers: yupObject({}).optional(), body: yupObject({ - access_token: yupString().defined(), - refresh_token: yupString().defined(), + id: yupString().defined(), user_id: yupString().defined(), + team_id: yupString().defined(), }).defined(), }, }, }, - }, - '/auth/password/set': { - POST: { - client: { - input: { - body: yupObject({ password: yupString().defined() }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, + DELETE: { server: { input: { - body: yupObject({ password: yupString().defined() }).defined(), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), }, - output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { - body: yupObject({ password: yupString().defined() }).defined(), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().defined(), + user_id: yupString().defined(), + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), }, - output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/auth/password/send-reset-code': { + '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: { client: { input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', - body: yupObject({ - success: yupString() - .defined() - .oneOf(['maybe, only if user with e-mail exists']), - }).defined(), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, server: { input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', - body: yupObject({ - success: yupString() - .defined() - .oneOf(['maybe, only if user with e-mail exists']), - }).defined(), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, admin: { input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', - body: yupObject({ - success: yupString() - .defined() - .oneOf(['maybe, only if user with e-mail exists']), - }).defined(), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, }, }, - '/auth/password/reset': { + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { POST: { - default: { + client: { input: { - body: yupObject({ - password: yupString().defined(), - code: yupString().defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).defined(), }, output: { statusCode: [200], bodyType: 'success' }, }, - }, - }, - '/auth/passkey/sign-in': { - POST: { - default: { + server: { input: { - body: yupObject({ - authentication_response: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, - }, - }, - '/auth/mfa/sign-in': { - POST: { - default: { + admin: { input: { - body: yupObject({ - type: yupString().defined().oneOf(['totp']), - totp: yupString().defined(), - code: yupString().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), + body: yupObject({ callback_url: yupString().defined() }).defined(), + params: yupObject({ user_id: yupString().defined(), + contact_channel_id: yupString().defined(), }).defined(), }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/auth/passkey/initiate-passkey-authentication': { - POST: { - client: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, - }, - server: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, - }, - admin: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, - }, - }, - }, - '/auth/passkey/initiate-passkey-registration': { + '/auth/sessions/current/refresh': { POST: { client: { input: {}, output: { statusCode: [200], - bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + bodyType: 'json', + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, server: { @@ -4818,10 +5109,7 @@ export const endpointSchema = { output: { statusCode: [200], bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, admin: { @@ -4829,51 +5117,26 @@ export const endpointSchema = { output: { statusCode: [200], bodyType: 'json', - body: yupObject({ - options_json: yupMixed().defined(), - code: yupString().defined(), - }).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, }, }, - '/auth/passkey/register': { + '/auth/otp/sign-in/check-code': { POST: { default: { - input: { - body: yupObject({ - credential: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, + input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ user_handle: yupString().defined() }).optional(), - }, - }, - }, - }, - '/auth/oauth/token': { - POST: { - default: { - input: { body: yupObject({ - grant_type: yupString() - .defined() - .oneOf(['authorization_code', 'refresh_token']), + is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), }, - output: { - statusCode: [], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, }, }, }, - '/auth/otp/sign-in': { + '/auth/password/reset/check-code': { POST: { default: { input: { body: yupObject({ code: yupString().defined() }).defined() }, @@ -4881,152 +5144,157 @@ export const endpointSchema = { statusCode: [200], bodyType: 'json', body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), - user_id: yupString().defined(), + is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), }, }, }, }, - '/auth/otp/send-sign-in-code': { - POST: { - client: { + '/auth/oauth/callback/[provider_id]': { + GET: { + default: { input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), }, output: { - statusCode: [200], + statusCode: [307], bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + headers: yupMixed().defined(), + body: yupMixed().defined(), }, }, - server: { + }, + POST: { + default: { input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), }, output: { - statusCode: [200], + statusCode: [307], bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + headers: yupMixed().defined(), + body: yupMixed().defined(), }, }, - admin: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, + }, + }, + '/integrations/neon/oauth': { + GET: { + default: { + input: {}, output: { statusCode: [200], - bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), + bodyType: 'text', + body: yupString().defined(), }, }, }, }, - '/team-permissions/[team_id]/[user_id]/[permission_id]': { - POST: { - server: { + '/integrations/neon/api-keys': { + GET: { + admin: { input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), + params: yupObject({ api_key_id: yupString().optional() }).optional(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), }).defined(), }, }, + }, + POST: { admin: { input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), }).defined(), }, }, }, - DELETE: { - server: { + }, + '/auth/oauth/authorize/[provider_id]': { + GET: { + default: { input: { query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), - permission_id: yupString().defined(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), + type: yupString().optional().oneOf(['authenticate', 'link']), + token: yupString().optional(), + provider_scope: yupString().optional(), + error_redirect_url: yupString().optional(), + error_redirect_uri: yupString().optional(), + after_callback_redirect_url: yupString().optional(), + client_id: yupString().defined(), + client_secret: yupString().defined(), + redirect_uri: yupString().defined(), + scope: yupString().defined(), + state: yupString().defined(), + grant_type: yupString().defined().oneOf(['authorization_code']), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined(), + response_type: yupString().defined(), + }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), }, + output: { statusCode: [302], bodyType: 'empty' }, }, - admin: { + }, + }, + '/integrations/neon/projects/provision': { + POST: { + default: { input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().defined(), - user_id: yupString().defined(), - permission_id: yupString().defined(), - }).optional(), + body: yupObject({ display_name: yupString().defined() }).defined(), }, output: { statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), + bodyType: 'json', + body: yupObject({ + project_id: yupString().defined(), + super_secret_admin_key: yupString().defined(), + }).defined(), }, }, }, @@ -5130,25 +5398,8 @@ export const endpointSchema = { client_id: yupString().optional(), client_secret: yupString().optional(), facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), - }, - }, - }, - }, - '/integrations/neon/projects/provision': { - POST: { - default: { - input: { - body: yupObject({ display_name: yupString().defined() }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - project_id: yupString().defined(), - super_secret_admin_key: yupString().defined(), - }).defined(), + microsoft_tenant_id: yupString().optional(), + }).optional(), }, }, }, @@ -5160,6 +5411,7 @@ export const endpointSchema = { body: yupObject({ interaction_uid: yupString().defined(), project_id: yupString().defined(), + neon_project_name: yupString().optional(), }).defined(), }, output: { @@ -5175,6 +5427,7 @@ export const endpointSchema = { body: yupObject({ interaction_uid: yupString().defined(), project_id: yupString().defined(), + neon_project_name: yupString().optional(), }).defined(), }, output: { @@ -5187,20 +5440,18 @@ export const endpointSchema = { }, }, }, - '/integrations/neon/oauth/authorize': { - GET: { + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: { default: { input: { - query: yupObject({ - client_id: yupString().defined(), - redirect_uri: yupString().defined(), - state: yupString().defined(), - code_challenge: yupString().defined(), - code_challenge_method: yupString().defined().oneOf(['S256']), - response_type: yupString().defined().oneOf(['code']), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [], + bodyType: 'json', + body: yupMixed().defined(), }, - output: {}, }, }, }, @@ -5219,6 +5470,23 @@ export const endpointSchema = { }, }, }, + '/integrations/neon/oauth/authorize': { + GET: { + default: { + input: { + query: yupObject({ + client_id: yupString().defined(), + redirect_uri: yupString().defined(), + state: yupString().defined(), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined().oneOf(['S256']), + response_type: yupString().defined().oneOf(['code']), + }).defined(), + }, + output: {}, + }, + }, + }, '/integrations/neon/api-keys/[api_key_id]': { GET: { admin: { @@ -5281,201 +5549,22 @@ export const endpointSchema = { }, }, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { - POST: { - client: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - server: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - admin: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/auth/sessions/current/refresh': { - POST: { - client: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - server: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - admin: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { - POST: { - client: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/auth/password/reset/check-code': { + '/integrations/neon/projects/transfer/initiate': { POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), - }).defined(), - }, - }, - }, - }, - '/auth/oauth/authorize/[provider_id]': { - GET: { default: { input: { - query: yupObject({ - type: yupString().optional().oneOf(['authenticate', 'link']), - token: yupString().optional(), - provider_scope: yupString().optional(), - error_redirect_url: yupString().optional(), - error_redirect_uri: yupString().optional(), - after_callback_redirect_url: yupString().optional(), - client_id: yupString().defined(), - client_secret: yupString().defined(), - redirect_uri: yupString().defined(), - scope: yupString().defined(), - state: yupString().defined(), - grant_type: yupString().defined().oneOf(['authorization_code']), - code_challenge: yupString().defined(), - code_challenge_method: yupString().defined(), - response_type: yupString().defined(), - }).defined(), - params: yupObject({ provider_id: yupString().defined() }).defined(), + body: yupObject({ project_id: yupString().defined() }).defined(), }, - output: { statusCode: [302], bodyType: 'empty' }, - }, - }, - }, - '/auth/otp/sign-in/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + confirmation_url: yupString().defined(), }).defined(), }, }, }, }, - '/auth/oauth/callback/[provider_id]': { - GET: { - default: { - input: { - params: yupObject({ provider_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [307], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, - }, - }, - POST: { - default: { - input: { - params: yupObject({ provider_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [307], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, - }, - }, - }, - '/integrations/neon/oauth/idp/[[...route]]': {}, '/integrations/neon/projects/transfer/confirm': { POST: { default: { @@ -5488,35 +5577,5 @@ export const endpointSchema = { }, }, }, - '/integrations/neon/projects/transfer/initiate': { - POST: { - default: { - input: { - body: yupObject({ project_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - confirmation_url: yupString().defined(), - }).defined(), - }, - }, - }, - }, - '/auth/oauth/connected-accounts/[provider_id]/access-token': { - POST: { - default: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ provider_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [], - bodyType: 'json', - body: yupMixed().defined(), - }, - }, - }, - }, + '/integrations/neon/oauth/idp/[[...route]]': {}, }; diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 3dbee81a5..a262c6fca 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -40,7 +40,7 @@ export type EndpointsSchema = { export type EndpointHandlers = { [url: string]: { [method in (typeof allowedMethods)[number]]?: { - [overload: string]: (req: ParsedRequest, options?: { params: Promise> }) => Promise>, + [overload: string]: (req: ParsedRequest, options?: { params: Promise> }) => Promise, } }, }; @@ -58,7 +58,7 @@ type ExtractInputOutputFromEndpointsSchema< O extends keyof S[U][M], > = NonNullable[O] -type ParsedRequestFromSchema< +export type ParsedRequestFromSchema< S extends EndpointsSchema, url extends keyof S, method extends (typeof allowedMethods)[number], @@ -68,12 +68,16 @@ type ParsedRequestFromSchema< yup.InferType['input']['query']> > -type ParsedResponseFromSchema< +export type ParsedResponseFromSchema< S extends EndpointsSchema, url extends keyof S, method extends (typeof allowedMethods)[number], overload extends keyof S[url][method] -> = ParsedResponse['output']['body']>> +> = { + bodyType: yup.InferType['output']['bodyType']>, + statusCode: yup.InferType['output']['statusCode']>, + body: yup.InferType['output']['body']>, +} type EndpointHandlersFromSchema = { [url in keyof S]: { @@ -167,35 +171,11 @@ type ParsedRequest> = { body: Body, query: Query, }; -type ParsedResponse = { +type ParsedResponse = { statusCode: number, - headers?: Record, -} & ( - | { - bodyType?: undefined, - body?: Body, - } - | { - bodyType: "empty", - body?: undefined, - } - | { - bodyType: "text", - body: string, - } - | { - bodyType: "json", - body: Body, - } - | { - bodyType: "binary", - body: ArrayBuffer, - } - | { - bodyType: "success", - body?: undefined, - } -) + bodyType: "empty" | "text" | "json" | "binary" | "success" | undefined, + body?: any, +} async function convertRawToParsedRequest( req: NextRequest, @@ -235,7 +215,7 @@ async function convertParsedRequestToRaw(req: ParsedRequest, + response: ParsedResponse, schema: yup.Schema ): Promise { const requestId = generateSecureRandomString(80); @@ -250,7 +230,11 @@ async function convertRawToParsedResponse< >( res: NextResponse, schema: EndpointOutputSchema -): Promise>> { +): Promise<{ + statusCode: StatusCode, + bodyType: T, + body: yup.InferType, +}> { // TODO validate schema let contentType = res.headers.get("content-type"); if (contentType) { @@ -260,37 +244,37 @@ async function convertRawToParsedResponse< switch (contentType) { case "application/json": { return { - statusCode: res.status, + statusCode: res.status as StatusCode, body: await res.json(), - bodyType: "json", + bodyType: "json" as T, }; } case "text/plain": { return { - statusCode: res.status, + statusCode: res.status as StatusCode, body: await res.text(), - bodyType: "text", + bodyType: "text" as T, }; } case "binary": { return { - statusCode: res.status, + statusCode: res.status as StatusCode, body: await res.arrayBuffer(), - bodyType: "binary", + bodyType: "binary" as T, }; } case "success": { return { - statusCode: res.status, + statusCode: res.status as StatusCode, body: undefined, - bodyType: "success", + bodyType: "success" as T, }; } case undefined: { return { - statusCode: res.status, + statusCode: res.status as StatusCode, body: undefined, - bodyType: "empty", + bodyType: "empty" as T, }; } default: { @@ -331,7 +315,7 @@ function createEndpointHandlers< ) => ( req: ParsedRequest, options?: { params: Promise> } - ) => Promise>, + ) => Promise, ) { const endpointHandlers = {}; for (const [url, endpointMethods] of typedEntries(oldEndpointsSchema)) { @@ -368,7 +352,7 @@ export function createMigrationEndpointHandlers< ): EndpointHandlersFromSchema { return createEndpointHandlers( oldEndpointsSchema, - (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { + (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise => { // TODO add validation let transformedRequest = req; const transform = (transforms as any)[url]?.[method]?.[overload]; @@ -394,7 +378,7 @@ export function createEndpointHandlersFromRawEndpoints< >(rawEndpointHandlers: H, endpointsSchema: S): E { return createEndpointHandlers( endpointsSchema, - (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise> => { + (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise => { const endpoint = (rawEndpointHandlers as any)[url]?.[method]; if (!endpoint) { From fd83bda9e4e2a18319fe2e096f5ea164750fa9e6 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 27 Jan 2025 18:00:23 -0800 Subject: [PATCH 38/40] fixed type --- apps/backend/scripts/test.ts | 42 +++++++------------ .../src/route-handlers/migration-handler.tsx | 42 +++++++++++-------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index 61753c45a..e60fcae67 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -1,5 +1,5 @@ import { EndpointTransforms, EndpointsSchema, ParsedResponseFromSchema, RawEndpointsHandlers, TransformFn, createEndpointHandlersFromRawEndpoints, createMigrationEndpointHandlers } from "@/route-handlers/migration-handler"; -import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; +import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { NextRequest, NextResponse } from "next/server"; const schema1 = { @@ -13,8 +13,8 @@ const schema1 = { }).defined(), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']).defined(), + statusCode: [200], + bodyType: 'json', body: yupObject({ id: yupString().defined(), fullName: yupString().defined(), @@ -31,8 +31,8 @@ const schema1 = { body: yupObject({}), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']).defined(), + statusCode: [200], + bodyType: 'json', body: yupObject({}).defined(), }, }, @@ -50,8 +50,8 @@ const schema1 = { }).defined(), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']), + statusCode: [200], + bodyType: 'json', body: yupObject({ same: yupString().defined(), }).defined(), @@ -74,8 +74,8 @@ const schema2 = { }).defined(), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']).defined(), + statusCode: [200], + bodyType: 'json', body: yupObject({ id: yupString().defined(), }).defined(), @@ -89,8 +89,8 @@ const schema2 = { body: yupObject({}).defined(), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']).defined(), + statusCode: [200], + bodyType: 'json', body: yupObject({}).defined(), }, }, @@ -108,8 +108,8 @@ const schema2 = { }).defined(), }, output: { - statusCode: yupNumber().defined(), - bodyType: yupString().oneOf(['json']).defined(), + statusCode: [200], + bodyType: 'json', body: yupObject({ same: yupString().defined(), }).defined(), @@ -169,21 +169,7 @@ const endpointHandlers1 = createMigrationEndpointHandlers(schema1, schema2, endp }; }, }, - }, - '/same': { - 'POST': { - 'default': async ({ req, newEndpointHandlers }) => { - return { - statusCode: 200, - headers: {}, - body: { - same: req.body.same, - }, - bodyType: 'json', - }; - }, - }, - }, + } }); type x = EndpointTransforms; diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index a262c6fca..6c84f12c4 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -17,7 +17,7 @@ export type EndpointInputSchema< }; export type EndpointOutputSchema< - StatusCode extends number, + StatusCode extends number[], T extends BodyType, Body extends yup.Schema | undefined > = { @@ -74,8 +74,8 @@ export type ParsedResponseFromSchema< method extends (typeof allowedMethods)[number], overload extends keyof S[url][method] > = { - bodyType: yup.InferType['output']['bodyType']>, - statusCode: yup.InferType['output']['statusCode']>, + bodyType: ExtractInputOutputFromEndpointsSchema['output']['bodyType'], + statusCode: ExtractInputOutputFromEndpointsSchema['output']['statusCode'][number], body: yup.InferType['output']['body']>, } @@ -173,7 +173,7 @@ type ParsedRequest> = { }; type ParsedResponse = { statusCode: number, - bodyType: "empty" | "text" | "json" | "binary" | "success" | undefined, + bodyType: BodyType, body?: any, } @@ -224,14 +224,13 @@ async function convertParsedResponseToRaw( async function convertRawToParsedResponse< Body extends yup.Schema, - StatusCode extends number, + StatusCode extends number[], T extends BodyType, - Headers extends yup.Schema >( res: NextResponse, schema: EndpointOutputSchema ): Promise<{ - statusCode: StatusCode, + statusCode: StatusCode[number], bodyType: T, body: yup.InferType, }> { @@ -241,46 +240,55 @@ async function convertRawToParsedResponse< contentType = contentType.split(";")[0]; } + let result: ParsedResponse; + switch (contentType) { case "application/json": { - return { - statusCode: res.status as StatusCode, + result = { + statusCode: res.status, body: await res.json(), bodyType: "json" as T, }; + break; } case "text/plain": { - return { - statusCode: res.status as StatusCode, + result = { + statusCode: res.status, body: await res.text(), bodyType: "text" as T, }; + break; } case "binary": { - return { - statusCode: res.status as StatusCode, + result = { + statusCode: res.status, body: await res.arrayBuffer(), bodyType: "binary" as T, }; + break; } case "success": { - return { - statusCode: res.status as StatusCode, + result = { + statusCode: res.status, body: undefined, bodyType: "success" as T, }; + break; } case undefined: { - return { - statusCode: res.status as StatusCode, + result = { + statusCode: res.status, body: undefined, bodyType: "empty" as T, }; + break; } default: { throw new Error(`Unsupported content type: ${contentType}`); } } + + return result as any; } export function createMigrationRoute(endpointHandlers: EndpointHandlers) { From dd1ab6ee4401b1a10ec6b299a71c4276430a8f78 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 27 Jan 2025 18:14:37 -0800 Subject: [PATCH 39/40] fixed more types --- apps/backend/scripts/generate-schema.ts | 2 + apps/backend/scripts/test.ts | 34 +- apps/backend/src/app/api/v2/imports.ts | 184 +- apps/backend/src/app/api/v2/schema.ts | 2602 ++++++++++------- .../src/route-handlers/migration-handler.tsx | 2 +- 5 files changed, 1595 insertions(+), 1229 deletions(-) diff --git a/apps/backend/scripts/generate-schema.ts b/apps/backend/scripts/generate-schema.ts index f0ac0f17e..9947863f9 100644 --- a/apps/backend/scripts/generate-schema.ts +++ b/apps/backend/scripts/generate-schema.ts @@ -106,6 +106,8 @@ function endpointSchemaToTypeString(reqSchema: yup.SchemaFieldDescription, resSc const field = Object.entries((reqSchema as any).fields).find(([k]) => k === key); if (field && Object.keys((field[1] as any).fields || {}).length > 0) { inputFields += `${key}: ${schemaToTypeString(field[1] as any)},`; + } else { + inputFields += `${key}: yupObject({}),`; } } inputFields += "}"; diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index e60fcae67..465f7ede3 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -125,6 +125,16 @@ const exampleRawEndpointHandlers = { return NextResponse.json({ id: 'example-id' }); }, }, + '/tokens': { + 'POST': async (req: NextRequest) => { + return NextResponse.json({ test: 'example-token' }); + }, + }, + '/same': { + 'POST': async (req: NextRequest) => { + return NextResponse.json({ same: 'same' }); + }, + }, } satisfies RawEndpointsHandlers; @@ -197,14 +207,30 @@ const c = null as unknown as b; // }); -endpointHandlers1['/users']['POST']['default']({ +// endpointHandlers1['/users']['POST']['default']({ +// body: { +// fullName: 'John Doe', +// }, +// query: {}, +// headers: {}, +// method: 'POST', +// url: 'http://localhost:3000/users', +// }).then((res) => { +// console.log(res); +// }).catch((err) => { +// console.error(err); +// }); + +endpointHandlers1['/same']['POST']['default']({ body: { - fullName: 'John Doe', + same: 'same', + }, + query: { + same: 'same', }, - query: {}, headers: {}, method: 'POST', - url: 'http://localhost:3000/users', + url: 'http://localhost:3000/same', }).then((res) => { console.log(res); }).catch((err) => { diff --git a/apps/backend/src/app/api/v2/imports.ts b/apps/backend/src/app/api/v2/imports.ts index 296cb747a..9d746c15a 100644 --- a/apps/backend/src/app/api/v2/imports.ts +++ b/apps/backend/src/app/api/v2/imports.ts @@ -1,32 +1,33 @@ import { GET as iGet } from '../../api/v1/route'; import { GET as iUsersGet } from '../../api/v1/users/route'; import { POST as iUsersPost } from '../../api/v1/users/route'; -import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; import { GET as iTeamsGet } from '../../api/v1/teams/route'; import { POST as iTeamsPost } from '../../api/v1/teams/route'; -import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; -import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; import { GET as iTeamPermissionDefinitionsGet } from '../../api/v1/team-permission-definitions/route'; import { POST as iTeamPermissionDefinitionsPost } from '../../api/v1/team-permission-definitions/route'; +import { GET as iTeamMemberProfilesGet } from '../../api/v1/team-member-profiles/route'; +import { GET as iTeamPermissionsGet } from '../../api/v1/team-permissions/route'; +import { GET as iTeamInvitationsGet } from '../../api/v1/team-invitations/route'; import { GET as iEmailTemplatesGet } from '../../api/v1/email-templates/route'; import { GET as iContactChannelsGet } from '../../api/v1/contact-channels/route'; import { POST as iContactChannelsPost } from '../../api/v1/contact-channels/route'; -import { POST as iCheckFeatureSupportPost } from '../../api/v1/check-feature-support/route'; import { POST as iCheckVersionPost } from '../../api/v1/check-version/route'; +import { POST as iCheckFeatureSupportPost } from '../../api/v1/check-feature-support/route'; +import { POST as iWebhooksSvixTokenPost } from '../../api/v1/webhooks/svix-token/route'; import { GET as iUsersMeGet } from '../../api/v1/users/me/route'; import { DELETE as iUsersMeDelete } from '../../api/v1/users/me/route'; import { PATCH as iUsersMePatch } from '../../api/v1/users/me/route'; -import { GET as iUsersUserIdGet } from '../../api/v1/users/[user_id]/route'; -import { DELETE as iUsersUserIdDelete } from '../../api/v1/users/[user_id]/route'; -import { PATCH as iUsersUserIdPatch } from '../../api/v1/users/[user_id]/route'; -import { POST as iWebhooksSvixTokenPost } from '../../api/v1/webhooks/svix-token/route'; import { GET as iTeamsTeamIdGet } from '../../api/v1/teams/[team_id]/route'; import { DELETE as iTeamsTeamIdDelete } from '../../api/v1/teams/[team_id]/route'; import { PATCH as iTeamsTeamIdPatch } from '../../api/v1/teams/[team_id]/route'; -import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; -import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; +import { GET as iUsersUserIdGet } from '../../api/v1/users/[user_id]/route'; +import { DELETE as iUsersUserIdDelete } from '../../api/v1/users/[user_id]/route'; +import { PATCH as iUsersUserIdPatch } from '../../api/v1/users/[user_id]/route'; import { DELETE as iTeamPermissionDefinitionsPermissionIdDelete } from '../../api/v1/team-permission-definitions/[permission_id]/route'; import { PATCH as iTeamPermissionDefinitionsPermissionIdPatch } from '../../api/v1/team-permission-definitions/[permission_id]/route'; +import { POST as iTeamInvitationsSendCodePost } from '../../api/v1/team-invitations/send-code/route'; +import { POST as iTeamInvitationsAcceptPost } from '../../api/v1/team-invitations/accept/route'; +import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; import { GET as iProjectsCurrentGet } from '../../api/v1/projects/current/route'; import { DELETE as iProjectsCurrentDelete } from '../../api/v1/projects/current/route'; import { PATCH as iProjectsCurrentPatch } from '../../api/v1/projects/current/route'; @@ -34,67 +35,65 @@ import { POST as iInternalSendTestEmailPost } from '../../api/v1/internal/send-t import { GET as iInternalProjectsGet } from '../../api/v1/internal/projects/route'; import { POST as iInternalProjectsPost } from '../../api/v1/internal/projects/route'; import { GET as iInternalMetricsGet } from '../../api/v1/internal/metrics/route'; -import { DELETE as iTeamInvitationsIdDelete } from '../../api/v1/team-invitations/[id]/route'; import { GET as iInternalApiKeysGet } from '../../api/v1/internal/api-keys/route'; import { POST as iInternalApiKeysPost } from '../../api/v1/internal/api-keys/route'; import { GET as iEmailTemplatesTypeGet } from '../../api/v1/email-templates/[type]/route'; import { DELETE as iEmailTemplatesTypeDelete } from '../../api/v1/email-templates/[type]/route'; import { PATCH as iEmailTemplatesTypePatch } from '../../api/v1/email-templates/[type]/route'; +import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; import { POST as iContactChannelsVerifyPost } from '../../api/v1/contact-channels/verify/route'; import { POST as iAuthSessionsPost } from '../../api/v1/auth/sessions/route'; -import { POST as iContactChannelsSendVerificationCodePost } from '../../api/v1/contact-channels/send-verification-code/route'; import { POST as iTeamMembershipsTeamIdUserIdPost } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { DELETE as iTeamMembershipsTeamIdUserIdDelete } from '../../api/v1/team-memberships/[team_id]/[user_id]/route'; import { GET as iTeamMemberProfilesTeamIdUserIdGet } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { PATCH as iTeamMemberProfilesTeamIdUserIdPatch } from '../../api/v1/team-member-profiles/[team_id]/[user_id]/route'; import { POST as iTeamInvitationsAcceptDetailsPost } from '../../api/v1/team-invitations/accept/details/route'; import { POST as iTeamInvitationsAcceptCheckCodePost } from '../../api/v1/team-invitations/accept/check-code/route'; +import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; import { GET as iInternalApiKeysApiKeyIdGet } from '../../api/v1/internal/api-keys/[api_key_id]/route'; import { PATCH as iInternalApiKeysApiKeyIdPatch } from '../../api/v1/internal/api-keys/[api_key_id]/route'; +import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; +import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; +import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; import { POST as iContactChannelsVerifyCheckCodePost } from '../../api/v1/contact-channels/verify/check-code/route'; +import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; +import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; import { GET as iContactChannelsUserIdContactChannelIdGet } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { DELETE as iContactChannelsUserIdContactChannelIdDelete } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { PATCH as iContactChannelsUserIdContactChannelIdPatch } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/route'; import { DELETE as iAuthSessionsCurrentDelete } from '../../api/v1/auth/sessions/current/route'; -import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; -import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; +import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; import { POST as iAuthPasswordSignUpPost } from '../../api/v1/auth/password/sign-up/route'; import { POST as iAuthPasswordSignInPost } from '../../api/v1/auth/password/sign-in/route'; import { POST as iAuthPasswordSetPost } from '../../api/v1/auth/password/set/route'; -import { POST as iAuthPasswordUpdatePost } from '../../api/v1/auth/password/update/route'; -import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; import { POST as iAuthPasswordSendResetCodePost } from '../../api/v1/auth/password/send-reset-code/route'; -import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; -import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; -import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; +import { POST as iAuthPasswordResetPost } from '../../api/v1/auth/password/reset/route'; +import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; import { POST as iAuthPasskeyInitiatePasskeyRegistrationPost } from '../../api/v1/auth/passkey/initiate-passkey-registration/route'; import { POST as iAuthPasskeyInitiatePasskeyAuthenticationPost } from '../../api/v1/auth/passkey/initiate-passkey-authentication/route'; -import { POST as iAuthPasskeySignInPost } from '../../api/v1/auth/passkey/sign-in/route'; -import { POST as iIntegrationsNeonWebhooksPost } from '../../api/v1/integrations/neon/webhooks/route'; -import { GET as iIntegrationsNeonOauthProvidersGet } from '../../api/v1/integrations/neon/oauth-providers/route'; -import { POST as iIntegrationsNeonOauthProvidersPost } from '../../api/v1/integrations/neon/oauth-providers/route'; +import { POST as iAuthPasskeyRegisterPost } from '../../api/v1/auth/passkey/register/route'; +import { POST as iAuthOtpSignInPost } from '../../api/v1/auth/otp/sign-in/route'; +import { POST as iAuthOtpSendSignInCodePost } from '../../api/v1/auth/otp/send-sign-in-code/route'; +import { POST as iAuthOauthTokenPost } from '../../api/v1/auth/oauth/token/route'; +import { POST as iAuthMfaSignInPost } from '../../api/v1/auth/mfa/sign-in/route'; import { POST as iTeamPermissionsTeamIdUserIdPermissionIdPost } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; import { DELETE as iTeamPermissionsTeamIdUserIdPermissionIdDelete } from '../../api/v1/team-permissions/[team_id]/[user_id]/[permission_id]/route'; -import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; -import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; -import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; -import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; -import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; -import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; -import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; -import { GET as iIntegrationsNeonOauthGet } from '../../api/v1/integrations/neon/oauth/route'; -import { GET as iIntegrationsNeonApiKeysGet } from '../../api/v1/integrations/neon/api-keys/route'; -import { POST as iIntegrationsNeonApiKeysPost } from '../../api/v1/integrations/neon/api-keys/route'; -import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; import { POST as iIntegrationsNeonProjectsProvisionPost } from '../../api/v1/integrations/neon/projects/provision/route'; +import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; +import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; import { DELETE as iIntegrationsNeonOauthProvidersOauthProviderIdDelete } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; import { PATCH as iIntegrationsNeonOauthProvidersOauthProviderIdPatch } from '../../api/v1/integrations/neon/oauth-providers/[oauth_provider_id]/route'; import { POST as iIntegrationsNeonInternalConfirmPost } from '../../api/v1/integrations/neon/internal/confirm/route'; -import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; -import { POST as iIntegrationsNeonOauthTokenPost } from '../../api/v1/integrations/neon/oauth/token/route'; -import { GET as iIntegrationsNeonOauthAuthorizeGet } from '../../api/v1/integrations/neon/oauth/authorize/route'; +import { POST as iConnectedAccountsUserIdProviderIdAccessTokenPost } from '../../api/v1/connected-accounts/[user_id]/[provider_id]/access-token/route'; import { GET as iIntegrationsNeonApiKeysApiKeyIdGet } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; import { PATCH as iIntegrationsNeonApiKeysApiKeyIdPatch } from '../../api/v1/integrations/neon/api-keys/[api_key_id]/route'; +import { POST as iContactChannelsUserIdContactChannelIdSendVerificationCodePost } from '../../api/v1/contact-channels/[user_id]/[contact_channel_id]/send-verification-code/route'; +import { POST as iAuthSessionsCurrentRefreshPost } from '../../api/v1/auth/sessions/current/refresh/route'; +import { POST as iAuthPasswordResetCheckCodePost } from '../../api/v1/auth/password/reset/check-code/route'; +import { GET as iAuthOauthAuthorizeProviderIdGet } from '../../api/v1/auth/oauth/authorize/[provider_id]/route'; +import { POST as iAuthOtpSignInCheckCodePost } from '../../api/v1/auth/otp/sign-in/check-code/route'; +import { GET as iAuthOauthCallbackProviderIdGet } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; +import { POST as iAuthOauthCallbackProviderIdPost } from '../../api/v1/auth/oauth/callback/[provider_id]/route'; import { POST as iIntegrationsNeonProjectsTransferInitiatePost } from '../../api/v1/integrations/neon/projects/transfer/initiate/route'; import { POST as iIntegrationsNeonProjectsTransferConfirmPost } from '../../api/v1/integrations/neon/projects/transfer/confirm/route'; import { GET as iIntegrationsNeonOauthIdpRouteGet } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; @@ -104,44 +103,46 @@ import { DELETE as iIntegrationsNeonOauthIdpRouteDelete } from '../../api/v1/int import { PATCH as iIntegrationsNeonOauthIdpRoutePatch } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { OPTIONS as iIntegrationsNeonOauthIdpRouteOptions } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; import { HEAD as iIntegrationsNeonOauthIdpRouteHead } from '../../api/v1/integrations/neon/oauth/idp/[[...route]]/route'; +import { POST as iAuthOauthConnectedAccountsProviderIdAccessTokenPost } from '../../api/v1/auth/oauth/connected-accounts/[provider_id]/access-token/route'; export const endpoints = { '/': { GET: iGet }, '/users': { GET: iUsersGet, POST: iUsersPost }, - '/team-permissions': { GET: iTeamPermissionsGet }, '/teams': { GET: iTeamsGet, POST: iTeamsPost }, - '/team-member-profiles': { GET: iTeamMemberProfilesGet }, - '/team-invitations': { GET: iTeamInvitationsGet }, '/team-permission-definitions': { GET: iTeamPermissionDefinitionsGet, POST: iTeamPermissionDefinitionsPost, }, + '/team-member-profiles': { GET: iTeamMemberProfilesGet }, + '/team-permissions': { GET: iTeamPermissionsGet }, + '/team-invitations': { GET: iTeamInvitationsGet }, '/email-templates': { GET: iEmailTemplatesGet }, '/contact-channels': { GET: iContactChannelsGet, POST: iContactChannelsPost }, - '/check-feature-support': { POST: iCheckFeatureSupportPost }, '/check-version': { POST: iCheckVersionPost }, + '/check-feature-support': { POST: iCheckFeatureSupportPost }, + '/webhooks/svix-token': { POST: iWebhooksSvixTokenPost }, '/users/me': { GET: iUsersMeGet, DELETE: iUsersMeDelete, PATCH: iUsersMePatch, }, - '/users/[user_id]': { - GET: iUsersUserIdGet, - DELETE: iUsersUserIdDelete, - PATCH: iUsersUserIdPatch, - }, - '/webhooks/svix-token': { POST: iWebhooksSvixTokenPost }, '/teams/[team_id]': { GET: iTeamsTeamIdGet, DELETE: iTeamsTeamIdDelete, PATCH: iTeamsTeamIdPatch, }, - '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, - '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, + '/users/[user_id]': { + GET: iUsersUserIdGet, + DELETE: iUsersUserIdDelete, + PATCH: iUsersUserIdPatch, + }, '/team-permission-definitions/[permission_id]': { DELETE: iTeamPermissionDefinitionsPermissionIdDelete, PATCH: iTeamPermissionDefinitionsPermissionIdPatch, }, + '/team-invitations/send-code': { POST: iTeamInvitationsSendCodePost }, + '/team-invitations/accept': { POST: iTeamInvitationsAcceptPost }, + '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, '/projects/current': { GET: iProjectsCurrentGet, DELETE: iProjectsCurrentDelete, @@ -153,7 +154,6 @@ export const endpoints = { POST: iInternalProjectsPost, }, '/internal/metrics': { GET: iInternalMetricsGet }, - '/team-invitations/[id]': { DELETE: iTeamInvitationsIdDelete }, '/internal/api-keys': { GET: iInternalApiKeysGet, POST: iInternalApiKeysPost, @@ -163,11 +163,11 @@ export const endpoints = { DELETE: iEmailTemplatesTypeDelete, PATCH: iEmailTemplatesTypePatch, }, - '/contact-channels/verify': { POST: iContactChannelsVerifyPost }, - '/auth/sessions': { POST: iAuthSessionsPost }, '/contact-channels/send-verification-code': { POST: iContactChannelsSendVerificationCodePost, }, + '/contact-channels/verify': { POST: iContactChannelsVerifyPost }, + '/auth/sessions': { POST: iAuthSessionsPost }, '/team-memberships/[team_id]/[user_id]': { POST: iTeamMembershipsTeamIdUserIdPost, DELETE: iTeamMembershipsTeamIdUserIdDelete, @@ -182,70 +182,58 @@ export const endpoints = { '/team-invitations/accept/check-code': { POST: iTeamInvitationsAcceptCheckCodePost, }, + '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, '/internal/api-keys/[api_key_id]': { GET: iInternalApiKeysApiKeyIdGet, PATCH: iInternalApiKeysApiKeyIdPatch, }, + '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, + '/integrations/neon/oauth-providers': { + GET: iIntegrationsNeonOauthProvidersGet, + POST: iIntegrationsNeonOauthProvidersPost, + }, '/contact-channels/verify/check-code': { POST: iContactChannelsVerifyCheckCodePost, }, + '/integrations/neon/api-keys': { + GET: iIntegrationsNeonApiKeysGet, + POST: iIntegrationsNeonApiKeysPost, + }, '/contact-channels/[user_id]/[contact_channel_id]': { GET: iContactChannelsUserIdContactChannelIdGet, DELETE: iContactChannelsUserIdContactChannelIdDelete, PATCH: iContactChannelsUserIdContactChannelIdPatch, }, '/auth/sessions/current': { DELETE: iAuthSessionsCurrentDelete }, - '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, - '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, + '/auth/password/update': { POST: iAuthPasswordUpdatePost }, '/auth/password/sign-up': { POST: iAuthPasswordSignUpPost }, '/auth/password/sign-in': { POST: iAuthPasswordSignInPost }, '/auth/password/set': { POST: iAuthPasswordSetPost }, - '/auth/password/update': { POST: iAuthPasswordUpdatePost }, - '/auth/password/reset': { POST: iAuthPasswordResetPost }, '/auth/password/send-reset-code': { POST: iAuthPasswordSendResetCodePost }, - '/auth/oauth/token': { POST: iAuthOauthTokenPost }, - '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, - '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, + '/auth/password/reset': { POST: iAuthPasswordResetPost }, + '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, '/auth/passkey/initiate-passkey-registration': { POST: iAuthPasskeyInitiatePasskeyRegistrationPost, }, '/auth/passkey/initiate-passkey-authentication': { POST: iAuthPasskeyInitiatePasskeyAuthenticationPost, }, - '/auth/passkey/sign-in': { POST: iAuthPasskeySignInPost }, - '/integrations/neon/webhooks': { POST: iIntegrationsNeonWebhooksPost }, - '/integrations/neon/oauth-providers': { - GET: iIntegrationsNeonOauthProvidersGet, - POST: iIntegrationsNeonOauthProvidersPost, - }, + '/auth/passkey/register': { POST: iAuthPasskeyRegisterPost }, + '/auth/otp/sign-in': { POST: iAuthOtpSignInPost }, + '/auth/otp/send-sign-in-code': { POST: iAuthOtpSendSignInCodePost }, + '/auth/oauth/token': { POST: iAuthOauthTokenPost }, + '/auth/mfa/sign-in': { POST: iAuthMfaSignInPost }, '/team-permissions/[team_id]/[user_id]/[permission_id]': { POST: iTeamPermissionsTeamIdUserIdPermissionIdPost, DELETE: iTeamPermissionsTeamIdUserIdPermissionIdDelete, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { - POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, - }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { - POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, - }, - '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, - '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, - '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, - '/auth/oauth/callback/[provider_id]': { - GET: iAuthOauthCallbackProviderIdGet, - POST: iAuthOauthCallbackProviderIdPost, - }, - '/integrations/neon/oauth': { GET: iIntegrationsNeonOauthGet }, - '/integrations/neon/api-keys': { - GET: iIntegrationsNeonApiKeysGet, - POST: iIntegrationsNeonApiKeysPost, - }, - '/auth/oauth/authorize/[provider_id]': { - GET: iAuthOauthAuthorizeProviderIdGet, - }, '/integrations/neon/projects/provision': { POST: iIntegrationsNeonProjectsProvisionPost, }, + '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, + '/integrations/neon/oauth/authorize': { + GET: iIntegrationsNeonOauthAuthorizeGet, + }, '/integrations/neon/oauth-providers/[oauth_provider_id]': { DELETE: iIntegrationsNeonOauthProvidersOauthProviderIdDelete, PATCH: iIntegrationsNeonOauthProvidersOauthProviderIdPatch, @@ -253,17 +241,26 @@ export const endpoints = { '/integrations/neon/internal/confirm': { POST: iIntegrationsNeonInternalConfirmPost, }, - '/auth/oauth/connected-accounts/[provider_id]/access-token': { - POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, - }, - '/integrations/neon/oauth/token': { POST: iIntegrationsNeonOauthTokenPost }, - '/integrations/neon/oauth/authorize': { - GET: iIntegrationsNeonOauthAuthorizeGet, + '/connected-accounts/[user_id]/[provider_id]/access-token': { + POST: iConnectedAccountsUserIdProviderIdAccessTokenPost, }, '/integrations/neon/api-keys/[api_key_id]': { GET: iIntegrationsNeonApiKeysApiKeyIdGet, PATCH: iIntegrationsNeonApiKeysApiKeyIdPatch, }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: iContactChannelsUserIdContactChannelIdSendVerificationCodePost, + }, + '/auth/sessions/current/refresh': { POST: iAuthSessionsCurrentRefreshPost }, + '/auth/password/reset/check-code': { POST: iAuthPasswordResetCheckCodePost }, + '/auth/oauth/authorize/[provider_id]': { + GET: iAuthOauthAuthorizeProviderIdGet, + }, + '/auth/otp/sign-in/check-code': { POST: iAuthOtpSignInCheckCodePost }, + '/auth/oauth/callback/[provider_id]': { + GET: iAuthOauthCallbackProviderIdGet, + POST: iAuthOauthCallbackProviderIdPost, + }, '/integrations/neon/projects/transfer/initiate': { POST: iIntegrationsNeonProjectsTransferInitiatePost, }, @@ -279,4 +276,7 @@ export const endpoints = { OPTIONS: iIntegrationsNeonOauthIdpRouteOptions, HEAD: iIntegrationsNeonOauthIdpRouteHead, }, + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: iAuthOauthConnectedAccountsProviderIdAccessTokenPost, + }, }; diff --git a/apps/backend/src/app/api/v2/schema.ts b/apps/backend/src/app/api/v2/schema.ts index f7f693fcf..7dbf3ff08 100644 --- a/apps/backend/src/app/api/v2/schema.ts +++ b/apps/backend/src/app/api/v2/schema.ts @@ -11,7 +11,11 @@ export const endpointSchema = { '/': { GET: { default: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'text', @@ -24,6 +28,7 @@ export const endpointSchema = { GET: { server: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), limit: yupNumber().optional(), @@ -85,6 +90,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), limit: yupNumber().optional(), @@ -298,113 +304,11 @@ export const endpointSchema = { }, }, }, - '/team-permissions': { - GET: { - client: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - server: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - admin: { - input: { - query: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - recursive: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ - team_id: yupString().optional(), - user_id: yupString().optional(), - permission_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - id: yupString().defined(), - user_id: yupString().defined(), - team_id: yupString().defined(), - }).defined(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - }, '/teams': { GET: { client: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), add_current_user: yupString().optional().oneOf(['true', 'false']), @@ -434,6 +338,7 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), add_current_user: yupString().optional().oneOf(['true', 'false']), @@ -465,6 +370,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), add_current_user: yupString().optional().oneOf(['true', 'false']), @@ -587,10 +493,71 @@ export const endpointSchema = { }, }, }, + '/team-permission-definitions': { + GET: { + admin: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({ + permission_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: { + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), + }).defined(), + query: yupObject({}), + params: yupObject({ + permission_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), + }).defined(), + }, + }, + }, + }, '/team-member-profiles': { GET: { client: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -622,6 +589,7 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -688,6 +656,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -754,12 +723,22 @@ export const endpointSchema = { }, }, }, - '/team-invitations': { + '/team-permissions': { GET: { client: { input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), + body: yupObject({}), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), }, output: { statusCode: [200], @@ -769,9 +748,8 @@ export const endpointSchema = { items: yupArray( yupObject({ id: yupString().defined(), + user_id: yupString().defined(), team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -783,8 +761,18 @@ export const endpointSchema = { }, server: { input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), + body: yupObject({}), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), }, output: { statusCode: [200], @@ -794,9 +782,8 @@ export const endpointSchema = { items: yupArray( yupObject({ id: yupString().defined(), + user_id: yupString().defined(), team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -808,8 +795,18 @@ export const endpointSchema = { }, admin: { input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().optional() }).optional(), + body: yupObject({}), + query: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + recursive: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ + team_id: yupString().optional(), + user_id: yupString().optional(), + permission_id: yupString().optional(), + }).optional(), }, output: { statusCode: [200], @@ -819,9 +816,8 @@ export const endpointSchema = { items: yupArray( yupObject({ id: yupString().defined(), + user_id: yupString().defined(), team_id: yupString().defined(), - expires_at_millis: yupNumber().defined(), - recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -833,13 +829,13 @@ export const endpointSchema = { }, }, }, - '/team-permission-definitions': { + '/team-invitations': { GET: { - admin: { + client: { input: { - params: yupObject({ - permission_id: yupString().optional(), - }).optional(), + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), }, output: { statusCode: [200], @@ -849,10 +845,9 @@ export const endpointSchema = { items: yupArray( yupObject({ id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), }).defined(), ).defined(), is_paginated: yupBoolean().defined(), @@ -862,38 +857,66 @@ export const endpointSchema = { }).defined(), }, }, - }, - POST: { - admin: { + server: { input: { - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).optional(), - }).defined(), - params: yupObject({ - permission_id: yupString().optional(), - }).optional(), + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), }, output: { - statusCode: [201], + statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), - }).defined(), - }, - }, - }, - }, - '/email-templates': { - GET: { + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + admin: { + input: { + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + id: yupString().defined(), + team_id: yupString().defined(), + expires_at_millis: yupNumber().defined(), + recipient_email: yupString().defined(), + }).defined(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + }, + '/email-templates': { + GET: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ type: yupString() .optional() @@ -938,6 +961,7 @@ export const endpointSchema = { GET: { client: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), contact_channel_id: yupString().optional(), @@ -972,6 +996,7 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), contact_channel_id: yupString().optional(), @@ -1006,6 +1031,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), contact_channel_id: yupString().optional(), @@ -1143,10 +1169,30 @@ export const endpointSchema = { }, }, }, + '/check-version': { + POST: { + default: { + input: { + body: yupObject({ clientVersion: yupString().defined() }).optional(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupMixed().defined(), + }, + }, + }, + }, '/check-feature-support': { POST: { default: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'text', @@ -1155,16 +1201,19 @@ export const endpointSchema = { }, }, }, - '/check-version': { + '/webhooks/svix-token': { POST: { - default: { + admin: { input: { - body: yupObject({ clientVersion: yupString().defined() }).optional(), + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', - body: yupMixed().defined(), + headers: yupObject({}).optional(), + body: yupObject({ token: yupString().defined() }).defined(), }, }, }, @@ -1172,7 +1221,11 @@ export const endpointSchema = { '/users/me': { GET: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -1210,7 +1263,11 @@ export const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -1253,7 +1310,11 @@ export const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -1298,7 +1359,11 @@ export const endpointSchema = { }, DELETE: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success', @@ -1307,7 +1372,11 @@ export const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success', @@ -1316,7 +1385,11 @@ export const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success', @@ -1337,6 +1410,8 @@ export const endpointSchema = { otp_auth_enabled: yupBoolean().optional(), passkey_auth_enabled: yupBoolean().optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -1392,6 +1467,8 @@ export const endpointSchema = { totp_secret_base64: yupString().optional().nullable(), selected_team_id: yupString().optional().nullable(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -1452,6 +1529,8 @@ export const endpointSchema = { totp_secret_base64: yupString().optional().nullable(), selected_team_id: yupString().optional().nullable(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -1496,127 +1575,104 @@ export const endpointSchema = { }, }, }, - '/users/[user_id]': { + '/teams/[team_id]': { GET: { + client: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, server: { input: { + body: yupObject({}), query: yupObject({ - team_id: yupString().optional(), - limit: yupNumber().optional(), - cursor: yupString().optional(), - order_by: yupString().optional().oneOf(['signed_up_at']), - desc: yupBoolean().optional(), - query: yupString().optional(), + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), }).optional(), - params: yupObject({ user_id: yupString().defined() }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), id: yupString().defined(), - primary_email: yupString().nullable(), - primary_email_verified: yupBoolean().defined(), - primary_email_auth_enabled: yupBoolean().defined(), - display_name: yupString().nullable(), - selected_team: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).nullable(), - selected_team_id: yupString().nullable(), + display_name: yupString().defined(), profile_image_url: yupString().nullable(), - signed_up_at_millis: yupNumber().defined(), - has_password: yupBoolean().defined(), - otp_auth_enabled: yupBoolean().defined(), - passkey_auth_enabled: yupBoolean().defined(), - client_metadata: yupMixed().nullable(), - client_read_only_metadata: yupMixed().nullable(), - server_metadata: yupMixed().nullable(), - last_active_at_millis: yupNumber().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString().defined(), - account_id: yupString().defined(), - email: yupString().optional().nullable(), - }).defined(), - ).defined(), - auth_with_email: yupBoolean().defined(), - requires_totp_mfa: yupBoolean().defined(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), }).defined(), }, }, admin: { input: { + body: yupObject({}), query: yupObject({ - team_id: yupString().optional(), - limit: yupNumber().optional(), - cursor: yupString().optional(), - order_by: yupString().optional().oneOf(['signed_up_at']), - desc: yupBoolean().optional(), - query: yupString().optional(), + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), }).optional(), - params: yupObject({ user_id: yupString().defined() }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), id: yupString().defined(), - primary_email: yupString().nullable(), - primary_email_verified: yupBoolean().defined(), - primary_email_auth_enabled: yupBoolean().defined(), - display_name: yupString().nullable(), - selected_team: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).nullable(), - selected_team_id: yupString().nullable(), + display_name: yupString().defined(), profile_image_url: yupString().nullable(), - signed_up_at_millis: yupNumber().defined(), - has_password: yupBoolean().defined(), - otp_auth_enabled: yupBoolean().defined(), - passkey_auth_enabled: yupBoolean().defined(), - client_metadata: yupMixed().nullable(), - client_read_only_metadata: yupMixed().nullable(), - server_metadata: yupMixed().nullable(), - last_active_at_millis: yupNumber().defined(), - oauth_providers: yupArray( - yupObject({ - id: yupString().defined(), - account_id: yupString().defined(), - email: yupString().optional().nullable(), - }).defined(), - ).defined(), - auth_with_email: yupBoolean().defined(), - requires_totp_mfa: yupBoolean().defined(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), }).defined(), }, }, }, DELETE: { + client: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, server: { input: { + body: yupObject({}), query: yupObject({ - team_id: yupString().optional(), - limit: yupNumber().optional(), - cursor: yupString().optional(), - order_by: yupString().optional().oneOf(['signed_up_at']), - desc: yupBoolean().optional(), - query: yupString().optional(), + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), }).optional(), - params: yupObject({ user_id: yupString().defined() }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1627,15 +1683,12 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ - team_id: yupString().optional(), - limit: yupNumber().optional(), - cursor: yupString().optional(), - order_by: yupString().optional().oneOf(['signed_up_at']), - desc: yupBoolean().optional(), - query: yupString().optional(), + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), }).optional(), - params: yupObject({ user_id: yupString().defined() }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1646,33 +1699,108 @@ export const endpointSchema = { }, }, PATCH: { - server: { + client: { input: { body: yupObject({ - display_name: yupString().optional().nullable(), + display_name: yupString().optional(), profile_image_url: yupString().optional().nullable(), client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - server_metadata: yupMixed().optional().nullable(), - primary_email: yupString().optional().nullable(), - primary_email_verified: yupBoolean().optional(), - primary_email_auth_enabled: yupBoolean().optional(), - passkey_auth_enabled: yupBoolean().optional(), - password: yupString().optional().nullable(), - password_hash: yupString().optional(), - otp_auth_enabled: yupBoolean().optional(), - totp_secret_base64: yupString().optional().nullable(), - selected_team_id: yupString().optional().nullable(), }).defined(), query: yupObject({ - team_id: yupString().optional(), - limit: yupNumber().optional(), - cursor: yupString().optional(), - order_by: yupString().optional().oneOf(['signed_up_at']), - desc: yupBoolean().optional(), - query: yupString().optional(), + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), }).optional(), - params: yupObject({ user_id: yupString().defined() }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + server: { + input: { + body: yupObject({ + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + admin: { + input: { + body: yupObject({ + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + display_name: yupString().optional(), + profile_image_url: yupString().optional().nullable(), + client_metadata: yupMixed().optional().nullable(), + }).defined(), + query: yupObject({ + user_id: yupString().optional(), + add_current_user: yupString().optional().oneOf(['true', 'false']), + }).optional(), + params: yupObject({ team_id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).defined(), + }, + }, + }, + }, + '/users/[user_id]': { + GET: { + server: { + input: { + body: yupObject({}), + query: yupObject({ + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), + }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1717,22 +1845,7 @@ export const endpointSchema = { }, admin: { input: { - body: yupObject({ - display_name: yupString().optional().nullable(), - profile_image_url: yupString().optional().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - server_metadata: yupMixed().optional().nullable(), - primary_email: yupString().optional().nullable(), - primary_email_verified: yupBoolean().optional(), - primary_email_auth_enabled: yupBoolean().optional(), - passkey_auth_enabled: yupBoolean().optional(), - password: yupString().optional().nullable(), - password_hash: yupString().optional(), - otp_auth_enabled: yupBoolean().optional(), - totp_secret_base64: yupString().optional().nullable(), - selected_team_id: yupString().optional().nullable(), - }).defined(), + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), limit: yupNumber().optional(), @@ -1785,113 +1898,19 @@ export const endpointSchema = { }, }, }, - }, - '/webhooks/svix-token': { - POST: { - admin: { - input: {}, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/teams/[team_id]': { - GET: { - client: { - input: { - query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).defined(), - }, - }, - server: { - input: { - query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).defined(), - }, - }, - admin: { - input: { - query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), - id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).defined(), - }, - }, - }, DELETE: { - client: { - input: { - query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), - }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, server: { input: { + body: yupObject({}), query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1902,11 +1921,16 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1917,18 +1941,33 @@ export const endpointSchema = { }, }, PATCH: { - client: { + server: { input: { body: yupObject({ - display_name: yupString().optional(), + display_name: yupString().optional().nullable(), profile_image_url: yupString().optional().nullable(), client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), }).defined(), query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -1936,70 +1975,153 @@ export const endpointSchema = { headers: yupObject({}).optional(), body: yupObject({ id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), - }).defined(), - }, - }, - server: { + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), + profile_image_url: yupString().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { input: { body: yupObject({ - client_read_only_metadata: yupMixed().optional().nullable(), - server_metadata: yupMixed().optional().nullable(), - display_name: yupString().optional(), + display_name: yupString().optional().nullable(), profile_image_url: yupString().optional().nullable(), client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + server_metadata: yupMixed().optional().nullable(), + primary_email: yupString().optional().nullable(), + primary_email_verified: yupBoolean().optional(), + primary_email_auth_enabled: yupBoolean().optional(), + passkey_auth_enabled: yupBoolean().optional(), + password: yupString().optional().nullable(), + password_hash: yupString().optional(), + otp_auth_enabled: yupBoolean().optional(), + totp_secret_base64: yupString().optional().nullable(), + selected_team_id: yupString().optional().nullable(), }).defined(), query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), + team_id: yupString().optional(), + limit: yupNumber().optional(), + cursor: yupString().optional(), + order_by: yupString().optional().oneOf(['signed_up_at']), + desc: yupBoolean().optional(), + query: yupString().optional(), }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ user_id: yupString().defined() }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), id: yupString().defined(), - display_name: yupString().defined(), + primary_email: yupString().nullable(), + primary_email_verified: yupBoolean().defined(), + primary_email_auth_enabled: yupBoolean().defined(), + display_name: yupString().nullable(), + selected_team: yupObject({ + created_at_millis: yupNumber().defined(), + server_metadata: yupMixed().optional().nullable(), + id: yupString().defined(), + display_name: yupString().defined(), + profile_image_url: yupString().nullable(), + client_metadata: yupMixed().optional().nullable(), + client_read_only_metadata: yupMixed().optional().nullable(), + }).nullable(), + selected_team_id: yupString().nullable(), profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), + signed_up_at_millis: yupNumber().defined(), + has_password: yupBoolean().defined(), + otp_auth_enabled: yupBoolean().defined(), + passkey_auth_enabled: yupBoolean().defined(), + client_metadata: yupMixed().nullable(), + client_read_only_metadata: yupMixed().nullable(), + server_metadata: yupMixed().nullable(), + last_active_at_millis: yupNumber().defined(), + oauth_providers: yupArray( + yupObject({ + id: yupString().defined(), + account_id: yupString().defined(), + email: yupString().optional().nullable(), + }).defined(), + ).defined(), + auth_with_email: yupBoolean().defined(), + requires_totp_mfa: yupBoolean().defined(), }).defined(), }, }, + }, + }, + '/team-permission-definitions/[permission_id]': { + DELETE: { + admin: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({ + permission_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + }, + PATCH: { admin: { input: { body: yupObject({ - client_read_only_metadata: yupMixed().optional().nullable(), - server_metadata: yupMixed().optional().nullable(), - display_name: yupString().optional(), - profile_image_url: yupString().optional().nullable(), - client_metadata: yupMixed().optional().nullable(), + id: yupString().optional(), + description: yupString().optional(), + contained_permission_ids: yupArray( + yupString().defined(), + ).optional(), }).defined(), - query: yupObject({ - user_id: yupString().optional(), - add_current_user: yupString().optional().oneOf(['true', 'false']), + query: yupObject({}), + params: yupObject({ + permission_id: yupString().defined(), }).optional(), - params: yupObject({ team_id: yupString().defined() }).optional(), }, output: { statusCode: [200], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - created_at_millis: yupNumber().defined(), - server_metadata: yupMixed().optional().nullable(), id: yupString().defined(), - display_name: yupString().defined(), - profile_image_url: yupString().nullable(), - client_metadata: yupMixed().optional().nullable(), - client_read_only_metadata: yupMixed().optional().nullable(), + description: yupString().optional(), + contained_permission_ids: yupArray(yupString().defined()).defined(), }).defined(), }, }, @@ -2014,6 +2136,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -2031,6 +2155,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -2048,6 +2174,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -2063,7 +2191,11 @@ export const endpointSchema = { '/team-invitations/accept': { POST: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -2072,13 +2204,26 @@ export const endpointSchema = { }, }, }, - '/team-permission-definitions/[permission_id]': { + '/team-invitations/[id]': { DELETE: { - admin: { + client: { input: { - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'success', + headers: yupObject({}).optional(), + body: yupMixed().optional(), + }, + }, + server: { + input: { + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), }, output: { statusCode: [200], @@ -2087,30 +2232,17 @@ export const endpointSchema = { body: yupMixed().optional(), }, }, - }, - PATCH: { admin: { input: { - body: yupObject({ - id: yupString().optional(), - description: yupString().optional(), - contained_permission_ids: yupArray( - yupString().defined(), - ).optional(), - }).defined(), - params: yupObject({ - permission_id: yupString().defined(), - }).optional(), + body: yupObject({}), + query: yupObject({ team_id: yupString().defined() }).optional(), + params: yupObject({ id: yupString().defined() }).optional(), }, output: { statusCode: [200], - bodyType: 'json', + bodyType: 'success', headers: yupObject({}).optional(), - body: yupObject({ - id: yupString().defined(), - description: yupString().optional(), - contained_permission_ids: yupArray(yupString().defined()).defined(), - }).defined(), + body: yupMixed().optional(), }, }, }, @@ -2118,7 +2250,11 @@ export const endpointSchema = { '/projects/current': { GET: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -2157,7 +2293,11 @@ export const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -2196,7 +2336,11 @@ export const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -2291,7 +2435,11 @@ export const endpointSchema = { }, DELETE: { admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success', @@ -2365,6 +2513,8 @@ export const endpointSchema = { ).optional(), }).optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -2474,6 +2624,8 @@ export const endpointSchema = { sender_email: yupString().defined(), }).defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -2490,6 +2642,8 @@ export const endpointSchema = { GET: { client: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -2593,6 +2747,8 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -2696,6 +2852,8 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -2863,6 +3021,7 @@ export const endpointSchema = { ).optional(), }).optional(), }).defined(), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -3020,6 +3179,7 @@ export const endpointSchema = { ).optional(), }).optional(), }).defined(), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -3177,6 +3337,7 @@ export const endpointSchema = { ).optional(), }).optional(), }).defined(), + query: yupObject({}), params: yupObject({ projectId: yupString().optional() }).optional(), }, output: { @@ -3275,7 +3436,11 @@ export const endpointSchema = { '/internal/metrics': { GET: { admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -3302,50 +3467,12 @@ export const endpointSchema = { }, }, }, - '/team-invitations/[id]': { - DELETE: { - client: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - server: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - admin: { - input: { - query: yupObject({ team_id: yupString().defined() }).optional(), - params: yupObject({ id: yupString().defined() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'success', - headers: yupObject({}).optional(), - body: yupMixed().optional(), - }, - }, - }, - }, '/internal/api-keys': { GET: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ api_key_id: yupString().optional() }).optional(), }, output: { @@ -3389,6 +3516,8 @@ export const endpointSchema = { has_secret_server_key: yupBoolean().defined(), has_super_secret_admin_key: yupBoolean().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -3411,6 +3540,8 @@ export const endpointSchema = { GET: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ type: yupString() .defined() @@ -3445,6 +3576,8 @@ export const endpointSchema = { DELETE: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ type: yupString() .defined() @@ -3471,6 +3604,7 @@ export const endpointSchema = { content: yupMixed().optional(), subject: yupString().optional(), }).defined(), + query: yupObject({}), params: yupObject({ type: yupString() .defined() @@ -3503,38 +3637,65 @@ export const endpointSchema = { }, }, }, - '/contact-channels/verify': { + '/contact-channels/send-verification-code': { POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + client: { + input: { + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success' }, }, - }, - }, - '/auth/sessions': { - POST: { server: { input: { body: yupObject({ - user_id: yupString().defined(), - expires_in_millis: yupNumber().optional(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { - statusCode: [200], - bodyType: 'json', + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, + output: { statusCode: [200], bodyType: 'success' }, }, - admin: { + }, + }, + '/contact-channels/verify': { + POST: { + default: { + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/sessions': { + POST: { + server: { input: { body: yupObject({ user_id: yupString().defined(), expires_in_millis: yupNumber().optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -3545,36 +3706,23 @@ export const endpointSchema = { }).defined(), }, }, - }, - }, - '/contact-channels/send-verification-code': { - POST: { - client: { - input: { - body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { + admin: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + user_id: yupString().defined(), + expires_in_millis: yupNumber().optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { + output: { + statusCode: [200], + bodyType: 'json', body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + refresh_token: yupString().defined(), + access_token: yupString().defined(), }).defined(), }, - output: { statusCode: [200], bodyType: 'success' }, }, }, }, @@ -3582,6 +3730,8 @@ export const endpointSchema = { POST: { server: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -3599,6 +3749,8 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -3618,6 +3770,8 @@ export const endpointSchema = { DELETE: { client: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ team_id: yupString().defined(), user_id: yupString().defined(), @@ -3632,6 +3786,8 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ team_id: yupString().defined(), user_id: yupString().defined(), @@ -3646,6 +3802,8 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ team_id: yupString().defined(), user_id: yupString().defined(), @@ -3664,6 +3822,7 @@ export const endpointSchema = { GET: { client: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -3687,6 +3846,7 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -3745,6 +3905,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), team_id: yupString().optional(), @@ -3959,7 +4120,11 @@ export const endpointSchema = { '/team-invitations/accept/details': { POST: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -3974,7 +4139,11 @@ export const endpointSchema = { '/team-invitations/accept/check-code': { POST: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -3985,10 +4154,31 @@ export const endpointSchema = { }, }, }, + '/integrations/neon/webhooks': { + POST: { + default: { + input: { + body: yupObject({ + url: yupString().defined(), + description: yupString().optional(), + }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ secret: yupString().defined() }).defined(), + }, + }, + }, + }, '/internal/api-keys/[api_key_id]': { GET: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ api_key_id: yupString().defined() }).optional(), }, output: { @@ -4021,6 +4211,7 @@ export const endpointSchema = { description: yupString().optional(), revoked: yupBoolean().optional().oneOf([true]), }).defined(), + query: yupObject({}), params: yupObject({ api_key_id: yupString().defined() }).optional(), }, output: { @@ -4047,31 +4238,44 @@ export const endpointSchema = { }, }, }, - '/contact-channels/verify/check-code': { - POST: { + '/integrations/neon/oauth': { + GET: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], - bodyType: 'json', - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), - }).defined(), + bodyType: 'text', + body: yupString().defined(), }, }, }, }, - '/contact-channels/[user_id]/[contact_channel_id]': { + '/integrations/neon/oauth-providers': { GET: { - client: { + admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), + body: yupObject({}), + query: yupObject({}), params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), }).optional(), }, output: { @@ -4079,74 +4283,289 @@ export const endpointSchema = { bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + items: yupArray( + yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), }).defined(), }, }, - server: { + }, + POST: { + admin: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), + body: yupObject({ + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().optional().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), }).optional(), + query: yupObject({}), params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), + oauth_provider_id: yupString() + .optional() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), }).optional(), }, output: { - statusCode: [200], + statusCode: [201], bodyType: 'json', headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), - }).defined(), + id: yupString() + .defined() + .oneOf([ + 'google', + 'github', + 'microsoft', + 'spotify', + 'facebook', + 'discord', + 'gitlab', + 'bitbucket', + 'linkedin', + 'apple', + 'x', + ]), + type: yupString().defined().oneOf(['shared', 'standard']), + client_id: yupString().optional(), + client_secret: yupString().optional(), + facebook_config_id: yupString().optional(), + microsoft_tenant_id: yupString().optional(), + }).optional(), }, }, - admin: { + }, + }, + '/contact-channels/verify/check-code': { + POST: { + default: { input: { - query: yupObject({ - user_id: yupString().optional(), - contact_channel_id: yupString().optional(), - }).optional(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).optional(), + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), body: yupObject({ - user_id: yupString().defined(), - id: yupString().defined(), - value: yupString().defined(), - type: yupString().defined().oneOf(['email']), - used_for_auth: yupBoolean().defined(), - is_verified: yupBoolean().defined(), - is_primary: yupBoolean().defined(), + is_code_valid: yupBoolean().defined().oneOf([true]), }).defined(), }, }, }, - DELETE: { - client: { + }, + '/integrations/neon/api-keys': { + GET: { + admin: { input: { - query: yupObject({ - user_id: yupString().optional(), + body: yupObject({}), + query: yupObject({}), + params: yupObject({ api_key_id: yupString().optional() }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + items: yupArray( + yupObject({ + publishable_client_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + secret_server_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + super_secret_admin_key: yupObject({ + last_four: yupString().defined(), + }).optional(), + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + }).optional(), + ).defined(), + is_paginated: yupBoolean().defined(), + pagination: yupObject({ + next_cursor: yupString().nullable(), + }).optional(), + }).defined(), + }, + }, + }, + POST: { + admin: { + input: { + body: yupObject({ + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + has_publishable_client_key: yupBoolean().defined(), + has_secret_server_key: yupBoolean().defined(), + has_super_secret_admin_key: yupBoolean().defined(), + }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + id: yupString().defined(), + description: yupString().defined(), + expires_at_millis: yupNumber().defined(), + manually_revoked_at_millis: yupNumber().optional(), + created_at_millis: yupNumber().defined(), + publishable_client_key: yupString().optional(), + secret_server_key: yupString().optional(), + super_secret_admin_key: yupString().optional(), + }).defined(), + }, + }, + }, + }, + '/contact-channels/[user_id]/[contact_channel_id]': { + GET: { + client: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + server: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + admin: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), + contact_channel_id: yupString().optional(), + }).optional(), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).optional(), + }, + output: { + statusCode: [200], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ + user_id: yupString().defined(), + id: yupString().defined(), + value: yupString().defined(), + type: yupString().defined().oneOf(['email']), + used_for_auth: yupBoolean().defined(), + is_verified: yupBoolean().defined(), + is_primary: yupBoolean().defined(), + }).defined(), + }, + }, + }, + DELETE: { + client: { + input: { + body: yupObject({}), + query: yupObject({ + user_id: yupString().optional(), contact_channel_id: yupString().optional(), }).optional(), params: yupObject({ @@ -4163,6 +4582,7 @@ export const endpointSchema = { }, server: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), contact_channel_id: yupString().optional(), @@ -4181,6 +4601,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ user_id: yupString().optional(), contact_channel_id: yupString().optional(), @@ -4302,76 +4723,65 @@ export const endpointSchema = { '/auth/sessions/current': { DELETE: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success' }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success' }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/auth/otp/send-sign-in-code': { + '/auth/password/update': { POST: { client: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + old_password: yupString().defined(), + new_password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), - }, + output: { statusCode: [200], bodyType: 'success' }, }, server: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), + old_password: yupString().defined(), + new_password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), - }, + output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { body: yupObject({ - email: yupString().defined(), - callback_url: yupString().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ nonce: yupString().defined() }).defined(), - }, - }, - }, - }, - '/auth/otp/sign-in': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - refresh_token: yupString().defined(), - access_token: yupString().defined(), - is_new_user: yupBoolean().defined(), - user_id: yupString().defined(), + old_password: yupString().defined(), + new_password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, @@ -4384,6 +4794,8 @@ export const endpointSchema = { password: yupString().defined(), verification_callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4402,6 +4814,8 @@ export const endpointSchema = { password: yupString().defined(), verification_callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4420,6 +4834,8 @@ export const endpointSchema = { password: yupString().defined(), verification_callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4441,6 +4857,8 @@ export const endpointSchema = { email: yupString().defined(), password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4458,6 +4876,8 @@ export const endpointSchema = { email: yupString().defined(), password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4475,6 +4895,8 @@ export const endpointSchema = { email: yupString().defined(), password: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4493,62 +4915,24 @@ export const endpointSchema = { client: { input: { body: yupObject({ password: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'success' }, }, server: { input: { body: yupObject({ password: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'success' }, }, admin: { input: { body: yupObject({ password: yupString().defined() }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/auth/password/update': { - POST: { - client: { - input: { - body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ - old_password: yupString().defined(), - new_password: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/auth/password/reset': { - POST: { - default: { - input: { - body: yupObject({ - password: yupString().defined(), - code: yupString().defined(), - }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'success' }, }, @@ -4562,6 +4946,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4579,6 +4965,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4596,6 +4984,8 @@ export const endpointSchema = { email: yupString().defined(), callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4609,34 +4999,31 @@ export const endpointSchema = { }, }, }, - '/auth/oauth/token': { + '/auth/password/reset': { POST: { default: { input: { body: yupObject({ - grant_type: yupString() - .defined() - .oneOf(['authorization_code', 'refresh_token']), + password: yupString().defined(), + code: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { - statusCode: [], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, + output: { statusCode: [200], bodyType: 'success' }, }, }, }, - '/auth/mfa/sign-in': { + '/auth/passkey/sign-in': { POST: { default: { input: { body: yupObject({ - type: yupString().defined().oneOf(['totp']), - totp: yupString().defined(), + authentication_response: yupMixed().defined(), code: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4651,27 +5038,14 @@ export const endpointSchema = { }, }, }, - '/auth/passkey/register': { - POST: { - default: { - input: { - body: yupObject({ - credential: yupMixed().defined(), - code: yupString().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ user_handle: yupString().defined() }).optional(), - }, - }, - }, - }, '/auth/passkey/initiate-passkey-registration': { POST: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4682,7 +5056,11 @@ export const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4693,7 +5071,11 @@ export const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4708,7 +5090,11 @@ export const endpointSchema = { '/auth/passkey/initiate-passkey-authentication': { POST: { client: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4719,7 +5105,11 @@ export const endpointSchema = { }, }, server: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4730,7 +5120,11 @@ export const endpointSchema = { }, }, admin: { - input: {}, + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -4742,14 +5136,32 @@ export const endpointSchema = { }, }, }, - '/auth/passkey/sign-in': { + '/auth/passkey/register': { POST: { default: { input: { body: yupObject({ - authentication_response: yupMixed().defined(), + credential: yupMixed().defined(), code: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ user_handle: yupString().defined() }).optional(), + }, + }, + }, + }, + '/auth/otp/sign-in': { + POST: { + default: { + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -4764,151 +5176,97 @@ export const endpointSchema = { }, }, }, - '/integrations/neon/webhooks': { + '/auth/otp/send-sign-in-code': { POST: { - default: { + client: { input: { body: yupObject({ - url: yupString().defined(), - description: yupString().optional(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'json', - body: yupObject({ secret: yupString().defined() }).defined(), + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, - }, - }, - '/integrations/neon/oauth-providers': { - GET: { - admin: { + server: { input: { - params: yupObject({ - oauth_provider_id: yupString() - .optional() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).optional(), + body: yupObject({ + email: yupString().defined(), + callback_url: yupString().defined(), + }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], bodyType: 'json', - headers: yupObject({}).optional(), + body: yupObject({ nonce: yupString().defined() }).defined(), + }, + }, + admin: { + input: { body: yupObject({ - items: yupArray( - yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), + email: yupString().defined(), + callback_url: yupString().defined(), }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ nonce: yupString().defined() }).defined(), }, }, }, + }, + '/auth/oauth/token': { POST: { - admin: { + default: { input: { body: yupObject({ - id: yupString() + grant_type: yupString() .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().optional().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), - params: yupObject({ - oauth_provider_id: yupString() - .optional() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - }).optional(), + .oneOf(['authorization_code', 'refresh_token']), + }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { - statusCode: [201], + statusCode: [], bodyType: 'json', - headers: yupObject({}).optional(), + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, + '/auth/mfa/sign-in': { + POST: { + default: { + input: { body: yupObject({ - id: yupString() - .defined() - .oneOf([ - 'google', - 'github', - 'microsoft', - 'spotify', - 'facebook', - 'discord', - 'gitlab', - 'bitbucket', - 'linkedin', - 'apple', - 'x', - ]), - type: yupString().defined().oneOf(['shared', 'standard']), - client_id: yupString().optional(), - client_secret: yupString().optional(), - facebook_config_id: yupString().optional(), - microsoft_tenant_id: yupString().optional(), - }).optional(), + type: yupString().defined().oneOf(['totp']), + totp: yupString().defined(), + code: yupString().defined(), + }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + refresh_token: yupString().defined(), + access_token: yupString().defined(), + is_new_user: yupBoolean().defined(), + user_id: yupString().defined(), + }).defined(), }, }, }, @@ -4917,6 +5275,7 @@ export const endpointSchema = { POST: { server: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -4942,6 +5301,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -4969,6 +5329,7 @@ export const endpointSchema = { DELETE: { server: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -4990,6 +5351,7 @@ export const endpointSchema = { }, admin: { input: { + body: yupObject({}), query: yupObject({ team_id: yupString().optional(), user_id: yupString().optional(), @@ -5011,291 +5373,58 @@ export const endpointSchema = { }, }, }, - '/connected-accounts/[user_id]/[provider_id]/access-token': { - POST: { - client: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - server: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - admin: { - input: { - body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ - provider_id: yupString().optional(), - user_id: yupString().optional(), - }).optional(), - }, - output: { - statusCode: [201], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { - POST: { - client: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - server: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - admin: { - input: { - body: yupObject({ callback_url: yupString().defined() }).defined(), - params: yupObject({ - user_id: yupString().defined(), - contact_channel_id: yupString().defined(), - }).defined(), - }, - output: { statusCode: [200], bodyType: 'success' }, - }, - }, - }, - '/auth/sessions/current/refresh': { - POST: { - client: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - server: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - admin: { - input: {}, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ access_token: yupString().defined() }).defined(), - }, - }, - }, - }, - '/auth/otp/sign-in/check-code': { + '/integrations/neon/projects/provision': { POST: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), - }).defined(), + input: { + body: yupObject({ display_name: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, - }, - }, - }, - '/auth/password/reset/check-code': { - POST: { - default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, output: { statusCode: [200], bodyType: 'json', body: yupObject({ - is_code_valid: yupBoolean().defined().oneOf([true]), + project_id: yupString().defined(), + super_secret_admin_key: yupString().defined(), }).defined(), }, }, }, }, - '/auth/oauth/callback/[provider_id]': { - GET: { - default: { - input: { - params: yupObject({ provider_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [307], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, - }, - }, + '/integrations/neon/oauth/token': { POST: { default: { - input: { - params: yupObject({ provider_id: yupString().defined() }).defined(), - }, - output: { - statusCode: [307], - bodyType: 'json', - headers: yupMixed().defined(), - body: yupMixed().defined(), - }, - }, - }, - }, - '/integrations/neon/oauth': { - GET: { - default: { - input: {}, - output: { - statusCode: [200], - bodyType: 'text', - body: yupString().defined(), - }, - }, - }, - }, - '/integrations/neon/api-keys': { - GET: { - admin: { - input: { - params: yupObject({ api_key_id: yupString().optional() }).optional(), - }, - output: { - statusCode: [200], - bodyType: 'json', - headers: yupObject({}).optional(), - body: yupObject({ - items: yupArray( - yupObject({ - publishable_client_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - secret_server_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - super_secret_admin_key: yupObject({ - last_four: yupString().defined(), - }).optional(), - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - }).optional(), - ).defined(), - is_paginated: yupBoolean().defined(), - pagination: yupObject({ - next_cursor: yupString().nullable(), - }).optional(), - }).defined(), - }, - }, - }, - POST: { - admin: { input: { body: yupObject({ - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - has_publishable_client_key: yupBoolean().defined(), - has_secret_server_key: yupBoolean().defined(), - has_super_secret_admin_key: yupBoolean().defined(), - }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - id: yupString().defined(), - description: yupString().defined(), - expires_at_millis: yupNumber().defined(), - manually_revoked_at_millis: yupNumber().optional(), - created_at_millis: yupNumber().defined(), - publishable_client_key: yupString().optional(), - secret_server_key: yupString().optional(), - super_secret_admin_key: yupString().optional(), - }).defined(), - }, - }, - }, - }, - '/auth/oauth/authorize/[provider_id]': { - GET: { - default: { - input: { - query: yupObject({ - type: yupString().optional().oneOf(['authenticate', 'link']), - token: yupString().optional(), - provider_scope: yupString().optional(), - error_redirect_url: yupString().optional(), - error_redirect_uri: yupString().optional(), - after_callback_redirect_url: yupString().optional(), - client_id: yupString().defined(), - client_secret: yupString().defined(), - redirect_uri: yupString().defined(), - scope: yupString().defined(), - state: yupString().defined(), grant_type: yupString().defined().oneOf(['authorization_code']), - code_challenge: yupString().defined(), - code_challenge_method: yupString().defined(), - response_type: yupString().defined(), + code: yupString().defined(), + code_verifier: yupString().defined(), + redirect_uri: yupString().defined(), }).defined(), - params: yupObject({ provider_id: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, - output: { statusCode: [302], bodyType: 'empty' }, + output: {}, }, }, }, - '/integrations/neon/projects/provision': { - POST: { + '/integrations/neon/oauth/authorize': { + GET: { default: { input: { - body: yupObject({ display_name: yupString().defined() }).defined(), - }, - output: { - statusCode: [200], - bodyType: 'json', - body: yupObject({ - project_id: yupString().defined(), - super_secret_admin_key: yupString().defined(), + body: yupObject({}), + query: yupObject({ + client_id: yupString().defined(), + redirect_uri: yupString().defined(), + state: yupString().defined(), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined().oneOf(['S256']), + response_type: yupString().defined().oneOf(['code']), }).defined(), + params: yupObject({}), }, + output: {}, }, }, }, @@ -5320,6 +5449,7 @@ export const endpointSchema = { 'x', ]), }).optional(), + query: yupObject({}), params: yupObject({ oauth_provider_id: yupString() .defined() @@ -5356,6 +5486,7 @@ export const endpointSchema = { facebook_config_id: yupString().optional(), microsoft_tenant_id: yupString().optional(), }).optional(), + query: yupObject({}), params: yupObject({ oauth_provider_id: yupString() .defined() @@ -5413,6 +5544,8 @@ export const endpointSchema = { project_id: yupString().defined(), neon_project_name: yupString().optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -5429,6 +5562,8 @@ export const endpointSchema = { project_id: yupString().defined(), neon_project_name: yupString().optional(), }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -5440,50 +5575,55 @@ export const endpointSchema = { }, }, }, - '/auth/oauth/connected-accounts/[provider_id]/access-token': { + '/connected-accounts/[user_id]/[provider_id]/access-token': { POST: { - default: { + client: { input: { body: yupObject({ scope: yupString().optional() }).defined(), - params: yupObject({ provider_id: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), }, output: { - statusCode: [], + statusCode: [201], bodyType: 'json', - body: yupMixed().defined(), + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, }, - }, - }, - '/integrations/neon/oauth/token': { - POST: { - default: { + server: { input: { - body: yupObject({ - grant_type: yupString().defined().oneOf(['authorization_code']), - code: yupString().defined(), - code_verifier: yupString().defined(), - redirect_uri: yupString().defined(), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + query: yupObject({}), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, - output: {}, }, - }, - }, - '/integrations/neon/oauth/authorize': { - GET: { - default: { + admin: { input: { - query: yupObject({ - client_id: yupString().defined(), - redirect_uri: yupString().defined(), - state: yupString().defined(), - code_challenge: yupString().defined(), - code_challenge_method: yupString().defined().oneOf(['S256']), - response_type: yupString().defined().oneOf(['code']), - }).defined(), + body: yupObject({ scope: yupString().optional() }).defined(), + query: yupObject({}), + params: yupObject({ + provider_id: yupString().optional(), + user_id: yupString().optional(), + }).optional(), + }, + output: { + statusCode: [201], + bodyType: 'json', + headers: yupObject({}).optional(), + body: yupObject({ access_token: yupString().defined() }).defined(), }, - output: {}, }, }, }, @@ -5491,6 +5631,8 @@ export const endpointSchema = { GET: { admin: { input: { + body: yupObject({}), + query: yupObject({}), params: yupObject({ api_key_id: yupString().defined() }).optional(), }, output: { @@ -5523,6 +5665,7 @@ export const endpointSchema = { description: yupString().optional(), revoked: yupBoolean().optional().oneOf([true]), }).defined(), + query: yupObject({}), params: yupObject({ api_key_id: yupString().defined() }).optional(), }, output: { @@ -5549,11 +5692,186 @@ export const endpointSchema = { }, }, }, + '/contact-channels/[user_id]/[contact_channel_id]/send-verification-code': { + POST: { + client: { + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + server: { + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + admin: { + input: { + body: yupObject({ callback_url: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({ + user_id: yupString().defined(), + contact_channel_id: yupString().defined(), + }).defined(), + }, + output: { statusCode: [200], bodyType: 'success' }, + }, + }, + }, + '/auth/sessions/current/refresh': { + POST: { + client: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + server: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + admin: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ access_token: yupString().defined() }).defined(), + }, + }, + }, + }, + '/auth/password/reset/check-code': { + POST: { + default: { + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/auth/oauth/authorize/[provider_id]': { + GET: { + default: { + input: { + body: yupObject({}), + query: yupObject({ + type: yupString().optional().oneOf(['authenticate', 'link']), + token: yupString().optional(), + provider_scope: yupString().optional(), + error_redirect_url: yupString().optional(), + error_redirect_uri: yupString().optional(), + after_callback_redirect_url: yupString().optional(), + client_id: yupString().defined(), + client_secret: yupString().defined(), + redirect_uri: yupString().defined(), + scope: yupString().defined(), + state: yupString().defined(), + grant_type: yupString().defined().oneOf(['authorization_code']), + code_challenge: yupString().defined(), + code_challenge_method: yupString().defined(), + response_type: yupString().defined(), + }).defined(), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { statusCode: [302], bodyType: 'empty' }, + }, + }, + }, + '/auth/otp/sign-in/check-code': { + POST: { + default: { + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, + output: { + statusCode: [200], + bodyType: 'json', + body: yupObject({ + is_code_valid: yupBoolean().defined().oneOf([true]), + }).defined(), + }, + }, + }, + }, + '/auth/oauth/callback/[provider_id]': { + GET: { + default: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [307], + bodyType: 'json', + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + POST: { + default: { + input: { + body: yupObject({}), + query: yupObject({}), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [307], + bodyType: 'json', + headers: yupMixed().defined(), + body: yupMixed().defined(), + }, + }, + }, + }, '/integrations/neon/projects/transfer/initiate': { POST: { default: { input: { body: yupObject({ project_id: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), }, output: { statusCode: [200], @@ -5568,7 +5886,11 @@ export const endpointSchema = { '/integrations/neon/projects/transfer/confirm': { POST: { default: { - input: { body: yupObject({ code: yupString().defined() }).defined() }, + input: { + body: yupObject({ code: yupString().defined() }).defined(), + query: yupObject({}), + params: yupObject({}), + }, output: { statusCode: [200], bodyType: 'json', @@ -5578,4 +5900,20 @@ export const endpointSchema = { }, }, '/integrations/neon/oauth/idp/[[...route]]': {}, + '/auth/oauth/connected-accounts/[provider_id]/access-token': { + POST: { + default: { + input: { + body: yupObject({ scope: yupString().optional() }).defined(), + query: yupObject({}), + params: yupObject({ provider_id: yupString().defined() }).defined(), + }, + output: { + statusCode: [], + bodyType: 'json', + body: yupMixed().defined(), + }, + }, + }, + }, }; diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 6c84f12c4..5423c3034 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -373,7 +373,7 @@ export function createMigrationEndpointHandlers< throw new Error(`No endpoint found for ${method.toString()} ${url.toString()}`); } - return endpoint(transformedRequest); + return endpoint[overload](transformedRequest); } } ); From 78459f135eb9d4a6092cadf92ede42a418e31018 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Mon, 27 Jan 2025 19:35:30 -0800 Subject: [PATCH 40/40] added schema validation --- apps/backend/scripts/test.ts | 40 ++++++++--------- .../src/route-handlers/migration-handler.tsx | 44 ++++++++++++++++--- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/apps/backend/scripts/test.ts b/apps/backend/scripts/test.ts index 465f7ede3..e0aa2a769 100644 --- a/apps/backend/scripts/test.ts +++ b/apps/backend/scripts/test.ts @@ -207,32 +207,32 @@ const c = null as unknown as b; // }); -// endpointHandlers1['/users']['POST']['default']({ -// body: { -// fullName: 'John Doe', -// }, -// query: {}, -// headers: {}, -// method: 'POST', -// url: 'http://localhost:3000/users', -// }).then((res) => { -// console.log(res); -// }).catch((err) => { -// console.error(err); -// }); - -endpointHandlers1['/same']['POST']['default']({ +endpointHandlers1['/users']['POST']['default']({ body: { - same: 'same', - }, - query: { - same: 'same', + fullName: 'John Doe', }, + query: {}, headers: {}, method: 'POST', - url: 'http://localhost:3000/same', + url: 'http://localhost:3000/users', }).then((res) => { console.log(res); }).catch((err) => { console.error(err); }); + +// endpointHandlers1['/same']['POST']['default']({ +// body: { +// same: 'same', +// }, +// query: { +// same: 'same', +// }, +// headers: {}, +// method: 'POST', +// url: 'http://localhost:3000/same', +// }).then((res) => { +// console.log(res); +// }).catch((err) => { +// console.error(err); +// }); diff --git a/apps/backend/src/route-handlers/migration-handler.tsx b/apps/backend/src/route-handlers/migration-handler.tsx index 5423c3034..959604995 100644 --- a/apps/backend/src/route-handlers/migration-handler.tsx +++ b/apps/backend/src/route-handlers/migration-handler.tsx @@ -291,6 +291,31 @@ async function convertRawToParsedResponse< return result as any; } +async function validateEndpointInput(input: EndpointInputSchema, req: ParsedRequest) { + try { + await yupValidate(input.body, req.body); + await yupValidate(input.query, req.query); + } catch (error) { + throw new Error(`Invalid request for ${req.method} ${req.url}\n\nOriginal error: ${error}`); + } +} + +async function validateEndpointOutput(output: EndpointOutputSchema, res: ParsedResponse) { + try { + await yupValidate(output.body, res.body); + if (!output.statusCode.includes(res.statusCode)) { + throw new Error(`Status code ${res.statusCode} not in ${output.statusCode.join(', ')}`); + } + if (output.bodyType !== res.bodyType) { + throw new Error(`Body type ${res.bodyType} not in ${output.bodyType}`); + } + return true; + } catch (error) { + throw new Error(`Invalid response for ${res.statusCode} ${res.bodyType}\n\nOriginal error: ${error}`); + } +} + + export function createMigrationRoute(endpointHandlers: EndpointHandlers) { return typedFromEntries(allowedMethods.map((method) => { return [method, async (req: NextRequest) => { @@ -319,7 +344,10 @@ function createEndpointHandlers< url: string, method: typeof allowedMethods[number], overload: string, - endpointSchema: EndpointInputSchema + endpointSchema: { + input: EndpointInputSchema, + output: EndpointOutputSchema, + } ) => ( req: ParsedRequest, options?: { params: Promise> } @@ -337,7 +365,10 @@ function createEndpointHandlers< url as string, method as typeof allowedMethods[number], overload as string, - endpointSchema as EndpointInputSchema + endpointSchema as { + input: EndpointInputSchema, + output: EndpointOutputSchema, + } ); } (urlHandlers as any)[method] = methodHandlers; @@ -361,11 +392,14 @@ export function createMigrationEndpointHandlers< return createEndpointHandlers( oldEndpointsSchema, (url, method, overload, endpointSchema) => async (req: ParsedRequest): Promise => { - // TODO add validation + let transformedRequest = req; const transform = (transforms as any)[url]?.[method]?.[overload]; if (transform) { - return transform({ req, newEndpointHandlers: newEndpointsHandlers }); + await validateEndpointInput(endpointSchema.input, transformedRequest); + const result = await transform({ req, newEndpointHandlers: newEndpointsHandlers }); + await validateEndpointOutput(endpointSchema.output, result); + return result; } else { const endpoint = (newEndpointsHandlers as any)[url]?.[method]; @@ -395,7 +429,7 @@ export function createEndpointHandlersFromRawEndpoints< const rawRequest = await convertParsedRequestToRaw(req); const rawResponse = await endpoint(rawRequest); - return await convertRawToParsedResponse(rawResponse, (endpointSchema as any).output); + return await convertRawToParsedResponse(rawResponse, endpointSchema.output); } ); }