From c2ef2271d8c5521a720f7ff92b4f2f86f05c758a Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Sun, 23 Mar 2025 01:31:50 +0800 Subject: [PATCH] feat: WIP add openapi-to-ts-client generator to automate the creation of agentic TS clients --- .vscode/launch.json | 35 + .../fixtures/generated/firecrawl-client.ts | 603 +++ .../fixtures/generated/pet-store-client.ts | 128 + .../fixtures/openapi/firecrawl.json | 936 ++++ .../fixtures/openapi/notion.json | 4448 +++++++++++++++++ .../fixtures/openapi/pet-store.json | 177 + .../fixtures/openapi/tic-tac-toe.json | 261 + packages/openapi-to-ts-client/package.json | 51 + packages/openapi-to-ts-client/readme.md | 24 + packages/openapi-to-ts-client/src/generate.ts | 614 +++ packages/openapi-to-ts-client/src/index.ts | 0 .../src/openapi-parameters-to-json-schema.ts | 221 + packages/openapi-to-ts-client/src/utils.ts | 253 + packages/openapi-to-ts-client/tsconfig.json | 5 + pnpm-lock.yaml | 231 +- 15 files changed, 7985 insertions(+), 2 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 packages/openapi-to-ts-client/fixtures/generated/firecrawl-client.ts create mode 100644 packages/openapi-to-ts-client/fixtures/generated/pet-store-client.ts create mode 100644 packages/openapi-to-ts-client/fixtures/openapi/firecrawl.json create mode 100644 packages/openapi-to-ts-client/fixtures/openapi/notion.json create mode 100644 packages/openapi-to-ts-client/fixtures/openapi/pet-store.json create mode 100644 packages/openapi-to-ts-client/fixtures/openapi/tic-tac-toe.json create mode 100644 packages/openapi-to-ts-client/package.json create mode 100644 packages/openapi-to-ts-client/readme.md create mode 100644 packages/openapi-to-ts-client/src/generate.ts create mode 100644 packages/openapi-to-ts-client/src/index.ts create mode 100644 packages/openapi-to-ts-client/src/openapi-parameters-to-json-schema.ts create mode 100644 packages/openapi-to-ts-client/src/utils.ts create mode 100644 packages/openapi-to-ts-client/tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8c89d3b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "tsx", + "type": "node", + "request": "launch", + + // Debug current file in VSCode + "program": "${file}", + + /* + * Path to tsx binary + * Assuming locally installed + */ + "runtimeExecutable": "tsx", + + /* + * Open terminal when debugging starts (Optional) + * Useful to see console.logs + */ + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + + // Files to exclude from debugger (e.g. call stack) + "skipFiles": [ + // Node.js internal core modules + "/**", + + // Ignore all dependencies (optional) + "${workspaceFolder}/node_modules/**" + ] + } + ] +} diff --git a/packages/openapi-to-ts-client/fixtures/generated/firecrawl-client.ts b/packages/openapi-to-ts-client/fixtures/generated/firecrawl-client.ts new file mode 100644 index 0000000..4d9f4d1 --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/generated/firecrawl-client.ts @@ -0,0 +1,603 @@ +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { aiFunction, AIFunctionsProvider, assert, getEnv } from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod' + +export namespace firecrawl { + export const apiBaseUrl = 'https://api.firecrawl.dev/v0' + + export const ScrapeResponseSchema = z.object({ + success: z.boolean().optional(), + /** Warning message to let you know of any issues. */ + warning: z + .string() + .nullable() + .describe('Warning message to let you know of any issues.') + .optional(), + data: z + .object({ + /** Markdown content of the page if the `markdown` format was specified (default) */ + markdown: z + .string() + .nullable() + .describe( + 'Markdown content of the page if the `markdown` format was specified (default)' + ) + .optional(), + /** HTML version of the content on page if the `html` format was specified */ + html: z + .string() + .nullable() + .describe( + 'HTML version of the content on page if the `html` format was specified' + ) + .optional(), + /** Raw HTML content of the page if the `rawHtml` format was specified */ + rawHtml: z + .string() + .nullable() + .describe( + 'Raw HTML content of the page if the `rawHtml` format was specified' + ) + .optional(), + /** Links on the page if the `links` format was specified */ + links: z + .array(z.string().url()) + .nullable() + .describe('Links on the page if the `links` format was specified') + .optional(), + /** URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified */ + screenshot: z + .string() + .nullable() + .describe( + 'URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified' + ) + .optional(), + metadata: z + .object({ + title: z.string().optional(), + description: z.string().optional(), + language: z.string().nullable().optional(), + sourceURL: z.string().url().optional(), + ' ': z.string().optional(), + /** The status code of the page */ + statusCode: z + .number() + .int() + .describe('The status code of the page') + .optional(), + /** The error message of the page */ + error: z + .string() + .nullable() + .describe('The error message of the page') + .optional() + }) + .optional() + }) + .optional() + }) + export type ScrapeResponse = z.infer + + export const CrawlResponseSchema = z.object({ + success: z.boolean().optional(), + id: z.string().optional(), + url: z.string().url().optional() + }) + export type CrawlResponse = z.infer + + export const SearchResponseSchema = z.object({ + success: z.boolean().optional(), + data: z.array(z.any()).optional() + }) + export type SearchResponse = z.infer + + export const CrawlStatusResponseObjSchema = z.object({ + /** Markdown content of the page if the `markdown` format was specified (default) */ + markdown: z + .string() + .nullable() + .describe( + 'Markdown content of the page if the `markdown` format was specified (default)' + ) + .optional(), + /** HTML version of the content on page if the `html` format was specified */ + html: z + .string() + .nullable() + .describe( + 'HTML version of the content on page if the `html` format was specified' + ) + .optional(), + /** Raw HTML content of the page if the `rawHtml` format was specified */ + rawHtml: z + .string() + .nullable() + .describe( + 'Raw HTML content of the page if the `rawHtml` format was specified' + ) + .optional(), + /** Links on the page if the `links` format was specified */ + links: z + .array(z.string().url()) + .nullable() + .describe('Links on the page if the `links` format was specified') + .optional(), + /** URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified */ + screenshot: z + .string() + .nullable() + .describe( + 'URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified' + ) + .optional(), + metadata: z + .object({ + title: z.string().optional(), + description: z.string().optional(), + language: z.string().nullable().optional(), + sourceURL: z.string().url().optional(), + ' ': z.string().optional(), + /** The status code of the page */ + statusCode: z + .number() + .int() + .describe('The status code of the page') + .optional(), + /** The error message of the page */ + error: z + .string() + .nullable() + .describe('The error message of the page') + .optional() + }) + .optional() + }) + export type CrawlStatusResponseObj = z.infer< + typeof CrawlStatusResponseObjSchema + > + + export const ScrapeParamsSchema = z.object({ + /** The URL to scrape */ + url: z.string().url().describe('The URL to scrape'), + /** + * Specific formats to return. + * + * - markdown: The page in Markdown format. + * - html: The page's HTML, trimmed to include only meaningful content. + * - rawHtml: The page's original HTML. + * - links: The links on the page. + * - screenshot: A screenshot of the top of the page. + * - screenshot@fullPage: A screenshot of the full page. (overridden by screenshot if present) + */ + formats: z + .array( + z.enum([ + 'markdown', + 'html', + 'rawHtml', + 'links', + 'screenshot', + 'screenshot@fullPage' + ]) + ) + .describe( + "Specific formats to return.\n\n - markdown: The page in Markdown format.\n - html: The page's HTML, trimmed to include only meaningful content.\n - rawHtml: The page's original HTML.\n - links: The links on the page.\n - screenshot: A screenshot of the top of the page.\n - screenshot@fullPage: A screenshot of the full page. (overridden by screenshot if present)" + ) + .default(['markdown']), + /** Headers to send with the request. Can be used to send cookies, user-agent, etc. */ + headers: z + .record(z.any()) + .describe( + 'Headers to send with the request. Can be used to send cookies, user-agent, etc.' + ) + .optional(), + /** Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer' */ + includeTags: z + .array(z.string()) + .describe( + "Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer'" + ) + .optional(), + /** Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer' */ + excludeTags: z + .array(z.string()) + .describe( + "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'" + ) + .optional(), + /** Only return the main content of the page excluding headers, navs, footers, etc. */ + onlyMainContent: z + .boolean() + .describe( + 'Only return the main content of the page excluding headers, navs, footers, etc.' + ) + .default(true), + /** Timeout in milliseconds for the request */ + timeout: z + .number() + .int() + .describe('Timeout in milliseconds for the request') + .default(30_000), + /** Wait x amount of milliseconds for the page to load to fetch content */ + waitFor: z + .number() + .int() + .describe( + 'Wait x amount of milliseconds for the page to load to fetch content' + ) + .default(0) + }) + export type ScrapeParams = z.infer + + export const CrawlUrlsParamsSchema = z.object({ + /** The base URL to start crawling from */ + url: z.string().url().describe('The base URL to start crawling from'), + crawlerOptions: z + .object({ + /** URL patterns to include */ + includes: z + .array(z.string()) + .describe('URL patterns to include') + .optional(), + /** URL patterns to exclude */ + excludes: z + .array(z.string()) + .describe('URL patterns to exclude') + .optional(), + /** Generate alt text for images using LLMs (must have a paid plan) */ + generateImgAltText: z + .boolean() + .describe( + 'Generate alt text for images using LLMs (must have a paid plan)' + ) + .default(false), + /** If true, returns only the URLs as a list on the crawl status. Attention: the return response will be a list of URLs inside the data, not a list of documents. */ + returnOnlyUrls: z + .boolean() + .describe( + 'If true, returns only the URLs as a list on the crawl status. Attention: the return response will be a list of URLs inside the data, not a list of documents.' + ) + .default(false), + /** Maximum depth to crawl relative to the entered URL. A maxDepth of 0 scrapes only the entered URL. A maxDepth of 1 scrapes the entered URL and all pages one level deep. A maxDepth of 2 scrapes the entered URL and all pages up to two levels deep. Higher values follow the same pattern. */ + maxDepth: z + .number() + .int() + .describe( + 'Maximum depth to crawl relative to the entered URL. A maxDepth of 0 scrapes only the entered URL. A maxDepth of 1 scrapes the entered URL and all pages one level deep. A maxDepth of 2 scrapes the entered URL and all pages up to two levels deep. Higher values follow the same pattern.' + ) + .optional(), + /** The crawling mode to use. Fast mode crawls 4x faster websites without sitemap, but may not be as accurate and shouldn't be used in heavy js-rendered websites. */ + mode: z + .enum(['default', 'fast']) + .describe( + "The crawling mode to use. Fast mode crawls 4x faster websites without sitemap, but may not be as accurate and shouldn't be used in heavy js-rendered websites." + ) + .default('default'), + /** Ignore the website sitemap when crawling */ + ignoreSitemap: z + .boolean() + .describe('Ignore the website sitemap when crawling') + .default(false), + /** Maximum number of pages to crawl */ + limit: z + .number() + .int() + .describe('Maximum number of pages to crawl') + .default(10_000), + /** Enables the crawler to navigate from a specific URL to previously linked pages. For instance, from 'example.com/product/123' back to 'example.com/product' */ + allowBackwardCrawling: z + .boolean() + .describe( + "Enables the crawler to navigate from a specific URL to previously linked pages. For instance, from 'example.com/product/123' back to 'example.com/product'" + ) + .default(false), + /** Allows the crawler to follow links to external websites. */ + allowExternalContentLinks: z + .boolean() + .describe('Allows the crawler to follow links to external websites.') + .default(false) + }) + .optional(), + pageOptions: z + .object({ + /** Headers to send with the request. Can be used to send cookies, user-agent, etc. */ + headers: z + .record(z.any()) + .describe( + 'Headers to send with the request. Can be used to send cookies, user-agent, etc.' + ) + .optional(), + /** Include the HTML version of the content on page. Will output a html key in the response. */ + includeHtml: z + .boolean() + .describe( + 'Include the HTML version of the content on page. Will output a html key in the response.' + ) + .default(false), + /** Include the raw HTML content of the page. Will output a rawHtml key in the response. */ + includeRawHtml: z + .boolean() + .describe( + 'Include the raw HTML content of the page. Will output a rawHtml key in the response.' + ) + .default(false), + /** Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer' */ + onlyIncludeTags: z + .array(z.string()) + .describe( + "Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer'" + ) + .optional(), + /** Only return the main content of the page excluding headers, navs, footers, etc. */ + onlyMainContent: z + .boolean() + .describe( + 'Only return the main content of the page excluding headers, navs, footers, etc.' + ) + .default(false), + /** Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer' */ + removeTags: z + .array(z.string()) + .describe( + "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'" + ) + .optional(), + /** Replace all relative paths with absolute paths for images and links */ + replaceAllPathsWithAbsolutePaths: z + .boolean() + .describe( + 'Replace all relative paths with absolute paths for images and links' + ) + .default(false), + /** Include a screenshot of the top of the page that you are scraping. */ + screenshot: z + .boolean() + .describe( + 'Include a screenshot of the top of the page that you are scraping.' + ) + .default(false), + /** Include a full page screenshot of the page that you are scraping. */ + fullPageScreenshot: z + .boolean() + .describe( + 'Include a full page screenshot of the page that you are scraping.' + ) + .default(false), + /** Wait x amount of milliseconds for the page to load to fetch content */ + waitFor: z + .number() + .int() + .describe( + 'Wait x amount of milliseconds for the page to load to fetch content' + ) + .default(0) + }) + .optional() + }) + export type CrawlUrlsParams = z.infer + + export const CrawlUrlsResponseSchema = CrawlResponseSchema + export type CrawlUrlsResponse = z.infer + + export const SearchGoogleParamsSchema = z.object({ + /** The query to search for */ + query: z.string().url().describe('The query to search for'), + pageOptions: z + .object({ + /** Only return the main content of the page excluding headers, navs, footers, etc. */ + onlyMainContent: z + .boolean() + .describe( + 'Only return the main content of the page excluding headers, navs, footers, etc.' + ) + .default(false), + /** Fetch the content of each page. If false, defaults to a basic fast serp API. */ + fetchPageContent: z + .boolean() + .describe( + 'Fetch the content of each page. If false, defaults to a basic fast serp API.' + ) + .default(true), + /** Include the HTML version of the content on page. Will output a html key in the response. */ + includeHtml: z + .boolean() + .describe( + 'Include the HTML version of the content on page. Will output a html key in the response.' + ) + .default(false), + /** Include the raw HTML content of the page. Will output a rawHtml key in the response. */ + includeRawHtml: z + .boolean() + .describe( + 'Include the raw HTML content of the page. Will output a rawHtml key in the response.' + ) + .default(false) + }) + .optional(), + searchOptions: z + .object({ + /** Maximum number of results. Max is 20 during beta. */ + limit: z + .number() + .int() + .describe('Maximum number of results. Max is 20 during beta.') + .optional() + }) + .optional() + }) + export type SearchGoogleParams = z.infer + + export const SearchGoogleResponseSchema = SearchResponseSchema + export type SearchGoogleResponse = z.infer + + export const GetCrawlStatusParamsSchema = z.object({ + /** ID of the crawl job */ + jobId: z.string().describe('ID of the crawl job') + }) + export type GetCrawlStatusParams = z.infer + + export const GetCrawlStatusResponseSchema = z.object({ + /** Status of the job (completed, active, failed, paused) */ + status: z + .string() + .describe('Status of the job (completed, active, failed, paused)') + .optional(), + /** Current page number */ + current: z.number().int().describe('Current page number').optional(), + /** Total number of pages */ + total: z.number().int().describe('Total number of pages').optional(), + /** Data returned from the job (null when it is in progress) */ + data: z + .array(CrawlStatusResponseObjSchema) + .describe('Data returned from the job (null when it is in progress)') + .optional(), + /** Partial documents returned as it is being crawled (streaming). **This feature is currently in alpha - expect breaking changes** When a page is ready, it will append to the partial_data array, so there is no need to wait for the entire website to be crawled. When the crawl is done, partial_data will become empty and the result will be available in `data`. There is a max of 50 items in the array response. The oldest item (top of the array) will be removed when the new item is added to the array. */ + partial_data: z + .array(CrawlStatusResponseObjSchema) + .describe( + 'Partial documents returned as it is being crawled (streaming). **This feature is currently in alpha - expect breaking changes** When a page is ready, it will append to the partial_data array, so there is no need to wait for the entire website to be crawled. When the crawl is done, partial_data will become empty and the result will be available in `data`. There is a max of 50 items in the array response. The oldest item (top of the array) will be removed when the new item is added to the array.' + ) + .optional() + }) + export type GetCrawlStatusResponse = z.infer< + typeof GetCrawlStatusResponseSchema + > + + export const CancelCrawlJobParamsSchema = z.object({ + /** ID of the crawl job */ + jobId: z.string().describe('ID of the crawl job') + }) + export type CancelCrawlJobParams = z.infer + + export const CancelCrawlJobResponseSchema = z.object({ + /** Returns cancelled. */ + status: z.string().describe('Returns cancelled.').optional() + }) + export type CancelCrawlJobResponse = z.infer< + typeof CancelCrawlJobResponseSchema + > +} + +export class FirecrawlClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + protected readonly apiKey: string + protected readonly apiBaseUrl: string + + constructor({ + apiKey = getEnv('FIRECRAWL_API_KEY'), + apiBaseUrl = firecrawl.apiBaseUrl, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + assert( + apiKey, + 'FirecrawlClient missing required "apiKey" (defaults to "FIRECRAWL_API_KEY")' + ) + super() + + this.apiKey = apiKey + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl, + headers: { + Authorization: apiKey + } + }) + } + + /** + * Scrape a single URL. + */ + @aiFunction({ + name: 'scrape', + description: 'Scrape a single URL.', + inputSchema: firecrawl.ScrapeParamsSchema + }) + async scrape( + params: firecrawl.ScrapeParams + ): Promise { + return this.ky + .post('/scrape', { + json: params + }) + .json() + } + + /** + * Crawl multiple URLs based on options. + */ + @aiFunction({ + name: 'crawl_urls', + description: 'Crawl multiple URLs based on options.', + inputSchema: firecrawl.CrawlUrlsParamsSchema + }) + async crawlUrls( + params: firecrawl.CrawlUrlsParams + ): Promise { + return this.ky + .post('/crawl', { + json: params + }) + .json() + } + + /** + * Search for a keyword in Google, returns top page results with markdown content for each page. + */ + @aiFunction({ + name: 'search_google', + description: + 'Search for a keyword in Google, returns top page results with markdown content for each page.', + inputSchema: firecrawl.SearchGoogleParamsSchema + }) + async searchGoogle( + params: firecrawl.SearchGoogleParams + ): Promise { + return this.ky + .post('/search', { + json: params + }) + .json() + } + + /** + * Get the status of a crawl job. + */ + @aiFunction({ + name: 'get_crawl_status', + description: 'Get the status of a crawl job.', + inputSchema: firecrawl.GetCrawlStatusParamsSchema + }) + async getCrawlStatus( + params: firecrawl.GetCrawlStatusParams + ): Promise { + return this.ky + .get(`/crawl/status/${params.jobId}`) + .json() + } + + /** + * Cancel a crawl job. + */ + @aiFunction({ + name: 'cancel_crawl_job', + description: 'Cancel a crawl job.', + inputSchema: firecrawl.CancelCrawlJobParamsSchema + }) + async cancelCrawlJob( + params: firecrawl.CancelCrawlJobParams + ): Promise { + return this.ky + .delete(`/crawl/cancel/${params.jobId}`) + .json() + } +} diff --git a/packages/openapi-to-ts-client/fixtures/generated/pet-store-client.ts b/packages/openapi-to-ts-client/fixtures/generated/pet-store-client.ts new file mode 100644 index 0000000..bc3355d --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/generated/pet-store-client.ts @@ -0,0 +1,128 @@ +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { + aiFunction, + AIFunctionsProvider, + sanitizeSearchParams +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod' + +export namespace petstore { + export const apiBaseUrl = 'http://petstore.swagger.io/v1' + + export const PetSchema = z.object({ + id: z.number().int(), + name: z.string(), + tag: z.string().optional() + }) + export type Pet = z.infer + + export const PetsSchema = z.array(PetSchema).max(100) + export type Pets = z.infer + + export const ListPetsParamsSchema = z.object({ + /** How many items to return at one time (max 100) */ + limit: z + .number() + .int() + .lte(100) + .describe('How many items to return at one time (max 100)') + .optional() + }) + export type ListPetsParams = z.infer + + export const ListPetsResponseSchema = PetsSchema + export type ListPetsResponse = z.infer + + export const CreatePetsParamsSchema = PetSchema + export type CreatePetsParams = z.infer + + export type CreatePetsResponse = undefined + + export const ShowPetByIdParamsSchema = z.object({ + /** The id of the pet to retrieve */ + petId: z.string().describe('The id of the pet to retrieve') + }) + export type ShowPetByIdParams = z.infer + + export const ShowPetByIdResponseSchema = PetSchema + export type ShowPetByIdResponse = z.infer +} + +export class PetStoreClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + + protected readonly apiBaseUrl: string + + constructor({ + apiBaseUrl = petstore.apiBaseUrl, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + super() + + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl + }) + } + + /** + * List all pets. + */ + @aiFunction({ + name: 'list_pets', + description: 'List all pets.', + inputSchema: petstore.ListPetsParamsSchema + }) + async listPets( + params: petstore.ListPetsParams + ): Promise { + return this.ky + .get('/pets', { + searchParams: sanitizeSearchParams(params) + }) + .json() + } + + /** + * Create a pet. + */ + @aiFunction({ + name: 'create_pets', + description: 'Create a pet.', + inputSchema: petstore.CreatePetsParamsSchema + }) + async createPets( + params: petstore.CreatePetsParams + ): Promise { + return this.ky + .post('/pets', { + json: params + }) + .json() + } + + /** + * Info for a specific pet. + */ + @aiFunction({ + name: 'show_pet_by_id', + description: 'Info for a specific pet.', + inputSchema: petstore.ShowPetByIdParamsSchema + }) + async showPetById( + params: petstore.ShowPetByIdParams + ): Promise { + return this.ky + .get(`/pets/${params.petId}`) + .json() + } +} diff --git a/packages/openapi-to-ts-client/fixtures/openapi/firecrawl.json b/packages/openapi-to-ts-client/fixtures/openapi/firecrawl.json new file mode 100644 index 0000000..fc9b8a4 --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/openapi/firecrawl.json @@ -0,0 +1,936 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Firecrawl API", + "version": "1.0.0", + "description": "API for interacting with Firecrawl services to perform web scraping and crawling tasks.", + "contact": { + "name": "Firecrawl Support", + "url": "https://firecrawl.dev/support", + "email": "support@firecrawl.dev" + } + }, + "servers": [ + { + "url": "https://api.firecrawl.dev/v0" + } + ], + "paths": { + "/scrape": { + "post": { + "summary": "Scrape a single URL", + "operationId": "scrape", + "tags": ["Scraping"], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The URL to scrape" + }, + "formats": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "markdown", + "html", + "rawHtml", + "links", + "screenshot", + "screenshot@fullPage" + ] + }, + "description": "Specific formats to return.\n\n - markdown: The page in Markdown format.\n - html: The page's HTML, trimmed to include only meaningful content.\n - rawHtml: The page's original HTML.\n - links: The links on the page.\n - screenshot: A screenshot of the top of the page.\n - screenshot@fullPage: A screenshot of the full page. (overridden by screenshot if present)", + "default": ["markdown"] + }, + "headers": { + "type": "object", + "description": "Headers to send with the request. Can be used to send cookies, user-agent, etc." + }, + "includeTags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer'" + }, + "excludeTags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'" + }, + "onlyMainContent": { + "type": "boolean", + "description": "Only return the main content of the page excluding headers, navs, footers, etc.", + "default": true + }, + "timeout": { + "type": "integer", + "description": "Timeout in milliseconds for the request", + "default": 30000 + }, + "waitFor": { + "type": "integer", + "description": "Wait x amount of milliseconds for the page to load to fetch content", + "default": 0 + } + }, + "required": ["url"] + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScrapeResponse" + } + } + } + }, + "402": { + "description": "Payment required", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Payment required to access this resource." + } + } + } + } + } + }, + "429": { + "description": "Too many requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Request rate limit exceeded. Please wait and try again later." + } + } + } + } + } + }, + "500": { + "description": "Server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "An unexpected error occurred on the server." + } + } + } + } + } + } + } + } + }, + "/crawl": { + "post": { + "summary": "Crawl multiple URLs based on options", + "operationId": "crawlUrls", + "tags": ["Crawling"], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The base URL to start crawling from" + }, + "crawlerOptions": { + "type": "object", + "properties": { + "includes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "URL patterns to include" + }, + "excludes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "URL patterns to exclude" + }, + "generateImgAltText": { + "type": "boolean", + "description": "Generate alt text for images using LLMs (must have a paid plan)", + "default": false + }, + "returnOnlyUrls": { + "type": "boolean", + "description": "If true, returns only the URLs as a list on the crawl status. Attention: the return response will be a list of URLs inside the data, not a list of documents.", + "default": false + }, + "maxDepth": { + "type": "integer", + "description": "Maximum depth to crawl relative to the entered URL. A maxDepth of 0 scrapes only the entered URL. A maxDepth of 1 scrapes the entered URL and all pages one level deep. A maxDepth of 2 scrapes the entered URL and all pages up to two levels deep. Higher values follow the same pattern." + }, + "mode": { + "type": "string", + "enum": ["default", "fast"], + "description": "The crawling mode to use. Fast mode crawls 4x faster websites without sitemap, but may not be as accurate and shouldn't be used in heavy js-rendered websites.", + "default": "default" + }, + "ignoreSitemap": { + "type": "boolean", + "description": "Ignore the website sitemap when crawling", + "default": false + }, + "limit": { + "type": "integer", + "description": "Maximum number of pages to crawl", + "default": 10000 + }, + "allowBackwardCrawling": { + "type": "boolean", + "description": "Enables the crawler to navigate from a specific URL to previously linked pages. For instance, from 'example.com/product/123' back to 'example.com/product'", + "default": false + }, + "allowExternalContentLinks": { + "type": "boolean", + "description": "Allows the crawler to follow links to external websites.", + "default": false + } + } + }, + "pageOptions": { + "type": "object", + "properties": { + "headers": { + "type": "object", + "description": "Headers to send with the request. Can be used to send cookies, user-agent, etc." + }, + "includeHtml": { + "type": "boolean", + "description": "Include the HTML version of the content on page. Will output a html key in the response.", + "default": false + }, + "includeRawHtml": { + "type": "boolean", + "description": "Include the raw HTML content of the page. Will output a rawHtml key in the response.", + "default": false + }, + "onlyIncludeTags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: 'script, .ad, #footer'" + }, + "onlyMainContent": { + "type": "boolean", + "description": "Only return the main content of the page excluding headers, navs, footers, etc.", + "default": false + }, + "removeTags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'" + }, + "replaceAllPathsWithAbsolutePaths": { + "type": "boolean", + "description": "Replace all relative paths with absolute paths for images and links", + "default": false + }, + "screenshot": { + "type": "boolean", + "description": "Include a screenshot of the top of the page that you are scraping.", + "default": false + }, + "fullPageScreenshot": { + "type": "boolean", + "description": "Include a full page screenshot of the page that you are scraping.", + "default": false + }, + "waitFor": { + "type": "integer", + "description": "Wait x amount of milliseconds for the page to load to fetch content", + "default": 0 + } + } + } + }, + "required": ["url"] + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CrawlResponse" + } + } + } + }, + "402": { + "description": "Payment required", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Payment required to access this resource." + } + } + } + } + } + }, + "429": { + "description": "Too many requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Request rate limit exceeded. Please wait and try again later." + } + } + } + } + } + }, + "500": { + "description": "Server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "An unexpected error occurred on the server." + } + } + } + } + } + } + } + } + }, + "/search": { + "post": { + "summary": "Search for a keyword in Google, returns top page results with markdown content for each page", + "operationId": "searchGoogle", + "tags": ["Search"], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "format": "uri", + "description": "The query to search for" + }, + "pageOptions": { + "type": "object", + "properties": { + "onlyMainContent": { + "type": "boolean", + "description": "Only return the main content of the page excluding headers, navs, footers, etc.", + "default": false + }, + "fetchPageContent": { + "type": "boolean", + "description": "Fetch the content of each page. If false, defaults to a basic fast serp API.", + "default": true + }, + "includeHtml": { + "type": "boolean", + "description": "Include the HTML version of the content on page. Will output a html key in the response.", + "default": false + }, + "includeRawHtml": { + "type": "boolean", + "description": "Include the raw HTML content of the page. Will output a rawHtml key in the response.", + "default": false + } + } + }, + "searchOptions": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "description": "Maximum number of results. Max is 20 during beta." + } + } + } + }, + "required": ["query"] + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchResponse" + } + } + } + }, + "402": { + "description": "Payment required", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Payment required to access this resource." + } + } + } + } + } + }, + "429": { + "description": "Too many requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Request rate limit exceeded. Please wait and try again later." + } + } + } + } + } + }, + "500": { + "description": "Server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "An unexpected error occurred on the server." + } + } + } + } + } + } + } + } + }, + "/crawl/status/{jobId}": { + "get": { + "tags": ["Crawl"], + "summary": "Get the status of a crawl job", + "operationId": "getCrawlStatus", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "jobId", + "in": "path", + "description": "ID of the crawl job", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "Status of the job (completed, active, failed, paused)" + }, + "current": { + "type": "integer", + "description": "Current page number" + }, + "total": { + "type": "integer", + "description": "Total number of pages" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CrawlStatusResponseObj" + }, + "description": "Data returned from the job (null when it is in progress)" + }, + "partial_data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CrawlStatusResponseObj" + }, + "description": "Partial documents returned as it is being crawled (streaming). **This feature is currently in alpha - expect breaking changes** When a page is ready, it will append to the partial_data array, so there is no need to wait for the entire website to be crawled. When the crawl is done, partial_data will become empty and the result will be available in `data`. There is a max of 50 items in the array response. The oldest item (top of the array) will be removed when the new item is added to the array." + } + } + } + } + } + }, + "402": { + "description": "Payment required", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Payment required to access this resource." + } + } + } + } + } + }, + "429": { + "description": "Too many requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Request rate limit exceeded. Please wait and try again later." + } + } + } + } + } + }, + "500": { + "description": "Server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "An unexpected error occurred on the server." + } + } + } + } + } + } + } + } + }, + "/crawl/cancel/{jobId}": { + "delete": { + "tags": ["Crawl"], + "summary": "Cancel a crawl job", + "operationId": "cancelCrawlJob", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "jobId", + "in": "path", + "description": "ID of the crawl job", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "Returns cancelled." + } + } + } + } + } + }, + "402": { + "description": "Payment required", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Payment required to access this resource." + } + } + } + } + } + }, + "429": { + "description": "Too many requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Request rate limit exceeded. Please wait and try again later." + } + } + } + } + } + }, + "500": { + "description": "Server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "An unexpected error occurred on the server." + } + } + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "ScrapeResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "warning": { + "type": "string", + "nullable": true, + "description": "Warning message to let you know of any issues." + }, + "data": { + "type": "object", + "properties": { + "markdown": { + "type": "string", + "nullable": true, + "description": "Markdown content of the page if the `markdown` format was specified (default)" + }, + "html": { + "type": "string", + "nullable": true, + "description": "HTML version of the content on page if the `html` format was specified" + }, + "rawHtml": { + "type": "string", + "nullable": true, + "description": "Raw HTML content of the page if the `rawHtml` format was specified" + }, + "links": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "nullable": true, + "description": "Links on the page if the `links` format was specified" + }, + "screenshot": { + "type": "string", + "nullable": true, + "description": "URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified" + }, + "metadata": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "language": { + "type": "string", + "nullable": true + }, + "sourceURL": { + "type": "string", + "format": "uri" + }, + " ": { + "type": "string" + }, + "statusCode": { + "type": "integer", + "description": "The status code of the page" + }, + "error": { + "type": "string", + "nullable": true, + "description": "The error message of the page" + } + } + } + } + } + } + }, + "CrawlStatusResponseObj": { + "type": "object", + "properties": { + "markdown": { + "type": "string", + "nullable": true, + "description": "Markdown content of the page if the `markdown` format was specified (default)" + }, + "html": { + "type": "string", + "nullable": true, + "description": "HTML version of the content on page if the `html` format was specified" + }, + "rawHtml": { + "type": "string", + "nullable": true, + "description": "Raw HTML content of the page if the `rawHtml` format was specified" + }, + "links": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "nullable": true, + "description": "Links on the page if the `links` format was specified" + }, + "screenshot": { + "type": "string", + "nullable": true, + "description": "URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified" + }, + "metadata": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "language": { + "type": "string", + "nullable": true + }, + "sourceURL": { + "type": "string", + "format": "uri" + }, + " ": { + "type": "string" + }, + "statusCode": { + "type": "integer", + "description": "The status code of the page" + }, + "error": { + "type": "string", + "nullable": true, + "description": "The error message of the page" + } + } + } + } + }, + "SearchResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "array", + "items": { + "markdown": { + "type": "string", + "nullable": true, + "description": "Markdown content of the page if the `markdown` format was specified (default)" + }, + "html": { + "type": "string", + "nullable": true, + "description": "HTML version of the content on page if the `html` format was specified" + }, + "rawHtml": { + "type": "string", + "nullable": true, + "description": "Raw HTML content of the page if the `rawHtml` format was specified" + }, + "links": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "nullable": true, + "description": "Links on the page if the `links` format was specified" + }, + "screenshot": { + "type": "string", + "nullable": true, + "description": "URL of the screenshot of the page if the `screenshot` or `screenshot@fullSize` format was specified" + }, + "metadata": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "language": { + "type": "string", + "nullable": true + }, + "sourceURL": { + "type": "string", + "format": "uri" + }, + " ": { + "type": "string" + }, + "statusCode": { + "type": "integer", + "description": "The status code of the page" + }, + "error": { + "type": "string", + "nullable": true, + "description": "The error message of the page" + } + } + } + } + } + } + }, + "CrawlResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] +} diff --git a/packages/openapi-to-ts-client/fixtures/openapi/notion.json b/packages/openapi-to-ts-client/fixtures/openapi/notion.json new file mode 100644 index 0000000..59abbd4 --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/openapi/notion.json @@ -0,0 +1,4448 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Notion API", + "version": "1.0.0", + "description": "API specification for Notion" + }, + "servers": [{ "url": "https://api.notion.so" }], + "security": [{ "oauth2": [] }], + + "paths": { + "/users/me": { + "get": { + "summary": "Get current user", + "operationId": "getSelf", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserObjectResponse" + } + } + } + } + } + } + }, + "/users/{user_id}": { + "get": { + "summary": "Get user", + "operationId": "getUser", + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserObjectResponse" + } + } + } + } + } + } + }, + "/users": { + "get": { + "summary": "List users", + "operationId": "listUsers", + "parameters": [ + { + "name": "start_cursor", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListUsersResponse" + } + } + } + } + } + } + }, + "/pages": { + "post": { + "summary": "Create page", + "operationId": "createPage", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePageParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialPageObjectResponse" + } + ] + } + } + } + } + } + } + }, + "/pages/{page_id}": { + "get": { + "summary": "Get page", + "operationId": "getPage", + "parameters": [ + { + "name": "page_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "filter_properties", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialPageObjectResponse" + } + ] + } + } + } + } + } + }, + "patch": { + "summary": "Update page", + "operationId": "updatePage", + "parameters": [ + { + "name": "page_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePageParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialPageObjectResponse" + } + ] + } + } + } + } + } + } + }, + "/pages/{page_id}/properties/{property_id}": { + "get": { + "summary": "Get page property", + "operationId": "getPageProperty", + "parameters": [ + { + "name": "page_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "property_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "start_cursor", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PropertyItemObjectResponse" + }, + { + "$ref": "#/components/schemas/PropertyItemListResponse" + } + ] + } + } + } + } + } + } + }, + "/blocks/{block_id}": { + "get": { + "summary": "Get block", + "operationId": "getBlock", + "parameters": [ + { + "name": "block_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialBlockObjectResponse" + }, + { + "$ref": "#/components/schemas/BlockObjectResponse" + } + ] + } + } + } + } + } + }, + "patch": { + "summary": "Update block", + "operationId": "updateBlock", + "parameters": [ + { + "name": "block_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateBlockParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialBlockObjectResponse" + }, + { + "$ref": "#/components/schemas/BlockObjectResponse" + } + ] + } + } + } + } + } + }, + "delete": { + "summary": "Delete block", + "operationId": "deleteBlock", + "parameters": [ + { + "name": "block_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialBlockObjectResponse" + }, + { + "$ref": "#/components/schemas/BlockObjectResponse" + } + ] + } + } + } + } + } + } + }, + "/blocks/{block_id}/children": { + "get": { + "summary": "List block children", + "operationId": "listBlockChildren", + "parameters": [ + { + "name": "block_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "start_cursor", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListBlockChildrenResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Append block children", + "operationId": "appendBlockChildren", + "parameters": [ + { + "name": "block_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AppendBlockChildrenParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AppendBlockChildrenResponse" + } + } + } + } + } + } + }, + "/databases/{database_id}": { + "get": { + "summary": "Get database", + "operationId": "getDatabase", + "parameters": [ + { + "name": "database_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialDatabaseObjectResponse" + }, + { + "$ref": "#/components/schemas/DatabaseObjectResponse" + } + ] + } + } + } + } + } + }, + "patch": { + "summary": "Update database", + "operationId": "updateDatabase", + "parameters": [ + { + "name": "database_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDatabaseParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialDatabaseObjectResponse" + }, + { + "$ref": "#/components/schemas/DatabaseObjectResponse" + } + ] + } + } + } + } + } + } + }, + "/databases/{database_id}/query": { + "post": { + "summary": "Query database", + "operationId": "queryDatabase", + "parameters": [ + { + "name": "database_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "filter_properties", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryDatabaseParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryDatabaseResponse" + } + } + } + } + } + } + }, + "/databases": { + "get": { + "summary": "List databases", + "operationId": "listDatabases", + "parameters": [ + { + "name": "start_cursor", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListDatabasesResponse" + } + } + } + } + } + }, + "post": { + "summary": "Create database", + "operationId": "createDatabase", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDatabaseParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialDatabaseObjectResponse" + }, + { + "$ref": "#/components/schemas/DatabaseObjectResponse" + } + ] + } + } + } + } + } + } + }, + "/search": { + "post": { + "summary": "Search", + "operationId": "search", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchResponse" + } + } + } + } + } + } + }, + "/comments": { + "post": { + "summary": "Create comment", + "operationId": "createComment", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCommentParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/CommentObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialCommentObjectResponse" + } + ] + } + } + } + } + } + }, + "get": { + "summary": "List comments", + "operationId": "listComments", + "parameters": [ + { + "name": "block_id", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "start_cursor", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListCommentsResponse" + } + } + } + } + } + } + }, + "/oauth/token": { + "post": { + "summary": "OAuth token", + "operationId": "oauthToken", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OauthTokenParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OauthTokenResponse" + } + } + } + } + } + } + } + }, + + "components": { + "securitySchemes": { + "oauth2": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "scopes": {}, + "authorizationUrl": "https://api.notion.com/v1/oauth/authorize", + "tokenUrl": "https://api.notion.com/v1/oauth/token" + } + } + } + }, + "schemas": { + "UserObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["user"] + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["person", "bot"] + }, + "name": { + "type": "string" + }, + "avatar_url": { + "type": "string" + } + }, + "required": ["object", "id", "type", "name", "avatar_url"] + }, + "ListUsersResponse": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserObjectResponse" + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["results", "next_cursor", "has_more"] + }, + "CreatePageParameters": { + "type": "object", + "properties": { + "parent": { + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id"] + }, + "page_id": { + "type": "string" + } + }, + "required": ["type", "page_id"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["database_id"] + }, + "database_id": { + "type": "string" + } + }, + "required": ["type", "database_id"] + } + ] + }, + "properties": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["title"] + }, + { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["rich_text"] + }, + { + "type": "object", + "properties": { + "number": { + "type": ["number", "null"] + } + }, + "required": ["number"] + }, + { + "type": "object", + "properties": { + "select": { + "type": ["object", "null"], + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + }, + "required": ["select"] + } + ] + } + } + }, + "required": ["parent", "properties"] + }, + "PageObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["page"] + }, + "id": { + "type": "string" + }, + "created_time": { + "type": "string" + }, + "last_edited_time": { + "type": "string" + }, + "archived": { + "type": "boolean" + }, + "url": { + "type": "string" + } + }, + "required": [ + "object", + "id", + "created_time", + "last_edited_time", + "archived", + "url" + ] + }, + "PartialPageObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["page"] + }, + "id": { + "type": "string" + } + }, + "required": ["object", "id"] + }, + "UpdatePageParameters": { + "type": "object", + "properties": { + "properties": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["title"] + }, + { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["rich_text"] + }, + { + "type": "object", + "properties": { + "number": { + "type": ["number", "null"] + } + }, + "required": ["number"] + }, + { + "type": "object", + "properties": { + "select": { + "type": ["object", "null"], + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + }, + "required": ["select"] + } + ] + } + }, + "archived": { + "type": "boolean" + } + } + }, + "PropertyItemObjectResponse": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + } + }, + "required": ["type", "id"] + }, + "PropertyItemListResponse": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyItemObjectResponse" + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["results", "next_cursor", "has_more"] + }, + "PartialBlockObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "id": { + "type": "string" + } + }, + "required": ["object", "id"] + }, + "BlockObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "created_time": { + "type": "string" + }, + "last_edited_time": { + "type": "string" + }, + "has_children": { + "type": "boolean" + }, + "archived": { + "type": "boolean" + } + }, + "required": [ + "object", + "id", + "type", + "created_time", + "last_edited_time", + "has_children", + "archived" + ] + }, + "UpdateBlockParameters": { + "type": "object", + "properties": { + "paragraph": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "heading_1": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "heading_2": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "heading_3": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "bulleted_list_item": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "numbered_list_item": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "quote": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "to_do": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "checked": { + "type": "boolean" + }, + "color": { + "type": "string" + } + } + }, + "toggle": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + } + }, + "code": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "language": { + "type": "string" + } + } + }, + "embed": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "image": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + } + }, + "video": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + } + }, + "file": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + } + }, + "pdf": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + } + }, + "bookmark": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "equation": { + "type": "object", + "properties": { + "expression": { + "type": "string" + } + } + }, + "divider": { + "type": "object" + }, + "table_of_contents": { + "type": "object", + "properties": { + "color": { + "type": "string" + } + } + }, + "breadcrumb": { + "type": "object" + }, + "column_list": { + "type": "object" + }, + "column": { + "type": "object" + }, + "link_to_page": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id", "database_id"] + }, + "page_id": { + "type": "string" + }, + "database_id": { + "type": "string" + } + } + }, + "table_row": { + "type": "object", + "properties": { + "cells": { + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + } + } + }, + "archived": { + "type": "boolean" + } + } + }, + "ListBlockChildrenResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialBlockObjectResponse" + }, + { + "$ref": "#/components/schemas/BlockObjectResponse" + } + ] + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "AppendBlockChildrenParameters": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BlockObjectRequest" + } + } + }, + "required": ["children"] + }, + "AppendBlockChildrenResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialBlockObjectResponse" + }, + { + "$ref": "#/components/schemas/BlockObjectResponse" + } + ] + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "PartialDatabaseObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["database"] + }, + "id": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/DatabasePropertyConfigResponse" + } + } + }, + "required": ["object", "id", "properties"] + }, + "DatabaseObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["database"] + }, + "id": { + "type": "string" + }, + "cover": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["external"] + }, + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["type", "external"] + }, + { + "type": "null" + } + ] + }, + "icon": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["emoji"] + }, + "emoji": { + "type": "string" + } + }, + "required": ["type", "emoji"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["external"] + }, + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["type", "external"] + }, + { + "type": "null" + } + ] + }, + "created_time": { + "type": "string" + }, + "created_by": { + "$ref": "#/components/schemas/PartialUserObjectResponse" + }, + "last_edited_time": { + "type": "string" + }, + "last_edited_by": { + "$ref": "#/components/schemas/PartialUserObjectResponse" + }, + "title": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemResponse" + } + }, + "description": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemResponse" + } + }, + "is_inline": { + "type": "boolean" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/DatabasePropertyConfigResponse" + } + }, + "parent": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id"] + }, + "page_id": { + "type": "string" + } + }, + "required": ["type", "page_id"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["workspace"] + }, + "workspace": { + "type": "boolean", + "enum": [true] + } + }, + "required": ["type", "workspace"] + } + ] + }, + "url": { + "type": "string" + }, + "archived": { + "type": "boolean" + } + }, + "required": [ + "object", + "id", + "created_time", + "created_by", + "last_edited_time", + "last_edited_by", + "title", + "description", + "is_inline", + "properties", + "parent", + "url", + "archived" + ] + }, + "UpdateDatabaseParameters": { + "type": "object", + "properties": { + "title": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "description": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "icon": { + "oneOf": [ + { + "type": "object", + "properties": { + "emoji": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["emoji"] + } + }, + "required": ["emoji", "type"] + }, + { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "type": { + "type": "string", + "enum": ["external"] + } + }, + "required": ["external", "type"] + }, + { + "type": "null" + } + ] + }, + "cover": { + "oneOf": [ + { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "type": { + "type": "string", + "enum": ["external"] + } + }, + "required": ["external", "type"] + }, + { + "type": "null" + } + ] + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/PropertyUpdateSchema" + } + }, + "is_inline": { + "type": "boolean" + }, + "archived": { + "type": "boolean" + } + } + }, + "QueryDatabaseParameters": { + "type": "object", + "properties": { + "sorts": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "direction": { + "type": "string", + "enum": ["ascending", "descending"] + } + }, + "required": ["property", "direction"] + }, + { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "enum": ["created_time", "last_edited_time"] + }, + "direction": { + "type": "string", + "enum": ["ascending", "descending"] + } + }, + "required": ["timestamp", "direction"] + } + ] + } + }, + "filter": { + "oneOf": [ + { + "$ref": "#/components/schemas/PropertyFilter" + }, + { + "$ref": "#/components/schemas/CompoundFilter" + } + ] + }, + "start_cursor": { + "type": "string" + }, + "page_size": { + "type": "integer" + }, + "archived": { + "type": "boolean" + } + } + }, + "QueryDatabaseResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialPageObjectResponse" + } + ] + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "ListDatabasesResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialDatabaseObjectResponse" + }, + { + "$ref": "#/components/schemas/DatabaseObjectResponse" + } + ] + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "CreateDatabaseParameters": { + "type": "object", + "properties": { + "parent": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id"] + }, + "page_id": { + "type": "string" + } + }, + "required": ["type", "page_id"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["database_id"] + }, + "database_id": { + "type": "string" + } + }, + "required": ["type", "database_id"] + } + ] + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/PropertySchema" + } + }, + "icon": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["emoji"] + }, + "emoji": { + "type": "string" + } + }, + "required": ["type", "emoji"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["external"] + }, + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["type", "external"] + }, + { + "type": "null" + } + ] + }, + "cover": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["external"] + }, + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["type", "external"] + }, + { + "type": "null" + } + ] + }, + "title": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "description": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "is_inline": { + "type": "boolean" + } + }, + "required": ["parent", "properties", "title"] + }, + "SearchParameters": { + "type": "object", + "properties": { + "query": { + "type": "string" + }, + "sort": { + "type": "object", + "properties": { + "direction": { + "type": "string", + "enum": ["ascending", "descending"] + }, + "timestamp": { + "type": "string", + "enum": ["last_edited_time"] + } + }, + "required": ["direction", "timestamp"] + }, + "filter": { + "type": "object", + "properties": { + "value": { + "type": "string", + "enum": ["page", "database"] + }, + "property": { + "type": "string", + "enum": ["object"] + } + }, + "required": ["value", "property"] + }, + "start_cursor": { + "type": "string" + }, + "page_size": { + "type": "integer" + } + } + }, + "SearchResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialPageObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialDatabaseObjectResponse" + }, + { + "$ref": "#/components/schemas/DatabaseObjectResponse" + } + ] + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "CreateCommentParameters": { + "oneOf": [ + { + "type": "object", + "properties": { + "parent": { + "type": "object", + "properties": { + "page_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["page_id"] + } + }, + "required": ["page_id"] + }, + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["parent", "rich_text"] + }, + { + "type": "object", + "properties": { + "discussion_id": { + "type": "string" + }, + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["discussion_id", "rich_text"] + } + ] + }, + "CommentObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["comment"] + }, + "id": { + "type": "string" + }, + "parent": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id"] + }, + "page_id": { + "type": "string" + } + }, + "required": ["type", "page_id"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["block_id"] + }, + "block_id": { + "type": "string" + } + }, + "required": ["type", "block_id"] + } + ] + }, + "discussion_id": { + "type": "string" + }, + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemResponse" + } + }, + "created_by": { + "$ref": "#/components/schemas/PartialUserObjectResponse" + }, + "created_time": { + "type": "string" + }, + "last_edited_time": { + "type": "string" + } + }, + "required": [ + "object", + "id", + "parent", + "discussion_id", + "rich_text", + "created_by", + "created_time", + "last_edited_time" + ] + }, + "PartialCommentObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["comment"] + }, + "id": { + "type": "string" + } + }, + "required": ["object", "id"] + }, + "ListCommentsResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["list"] + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CommentObjectResponse" + } + }, + "next_cursor": { + "type": ["string", "null"] + }, + "has_more": { + "type": "boolean" + } + }, + "required": ["object", "results", "next_cursor", "has_more"] + }, + "OauthTokenParameters": { + "type": "object", + "properties": { + "grant_type": { + "type": "string" + }, + "code": { + "type": "string" + }, + "redirect_uri": { + "type": "string" + }, + "external_account": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["key", "name"] + } + }, + "required": ["grant_type", "code"] + }, + "OauthTokenResponse": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "token_type": { + "type": "string", + "enum": ["bearer"] + }, + "bot_id": { + "type": "string" + }, + "workspace_name": { + "type": ["string", "null"] + }, + "workspace_icon": { + "type": ["string", "null"] + }, + "workspace_id": { + "type": "string" + }, + "owner": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["user"] + }, + "user": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserObjectResponse" + }, + { + "$ref": "#/components/schemas/PartialUserObjectResponse" + } + ] + } + }, + "required": ["type", "user"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["workspace"] + }, + "workspace": { + "type": "boolean", + "enum": [true] + } + }, + "required": ["type", "workspace"] + } + ] + }, + "duplicated_template_id": { + "type": ["string", "null"] + } + }, + "required": [ + "access_token", + "token_type", + "bot_id", + "workspace_name", + "workspace_icon", + "workspace_id", + "owner", + "duplicated_template_id" + ] + }, + "RichTextItemRequest": { + "oneOf": [ + { + "type": "object", + "properties": { + "text": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "link": { + "type": ["object", "null"], + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["content"] + }, + "type": { + "type": "string", + "enum": ["text"] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationRequest" + } + }, + "required": ["text"] + }, + { + "type": "object", + "properties": { + "mention": { + "oneOf": [ + { + "type": "object", + "properties": { + "user": { + "oneOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + }, + { + "$ref": "#/components/schemas/UserObjectResponse" + } + ] + } + }, + "required": ["user"] + }, + { + "type": "object", + "properties": { + "page": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + } + }, + "required": ["page"] + }, + { + "type": "object", + "properties": { + "database": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + } + }, + "required": ["database"] + }, + { + "type": "object", + "properties": { + "date": { + "$ref": "#/components/schemas/DateRequest" + } + }, + "required": ["date"] + } + ] + }, + "type": { + "type": "string", + "enum": ["mention"] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationRequest" + } + }, + "required": ["mention"] + }, + { + "type": "object", + "properties": { + "equation": { + "type": "object", + "properties": { + "expression": { + "type": "string" + } + }, + "required": ["expression"] + }, + "type": { + "type": "string", + "enum": ["equation"] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationRequest" + } + }, + "required": ["equation"] + } + ] + }, + "RichTextItemResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/TextRichTextItemResponse" + }, + { + "$ref": "#/components/schemas/MentionRichTextItemResponse" + }, + { + "$ref": "#/components/schemas/EquationRichTextItemResponse" + } + ] + }, + "AnnotationRequest": { + "type": "object", + "properties": { + "bold": { + "type": "boolean" + }, + "italic": { + "type": "boolean" + }, + "strikethrough": { + "type": "boolean" + }, + "underline": { + "type": "boolean" + }, + "code": { + "type": "boolean" + }, + "color": { + "type": "string", + "enum": [ + "default", + "gray", + "brown", + "orange", + "yellow", + "green", + "blue", + "purple", + "pink", + "red", + "gray_background", + "brown_background", + "orange_background", + "yellow_background", + "green_background", + "blue_background", + "purple_background", + "pink_background", + "red_background" + ] + } + } + }, + "DateRequest": { + "type": "object", + "properties": { + "start": { + "type": "string" + }, + "end": { + "type": ["string", "null"] + }, + "time_zone": { + "type": ["string", "null"] + } + }, + "required": ["start"] + }, + "TextRichTextItemResponse": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["text"] + }, + "text": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "link": { + "type": ["object", "null"], + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["content", "link"] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationResponse" + }, + "plain_text": { + "type": "string" + }, + "href": { + "type": ["string", "null"] + } + }, + "required": ["type", "text", "annotations", "plain_text", "href"] + }, + "MentionRichTextItemResponse": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["mention"] + }, + "mention": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["user"] + }, + "user": { + "oneOf": [ + { + "$ref": "#/components/schemas/PartialUserObjectResponse" + }, + { + "$ref": "#/components/schemas/UserObjectResponse" + } + ] + } + }, + "required": ["type", "user"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["date"] + }, + "date": { + "$ref": "#/components/schemas/DateResponse" + } + }, + "required": ["type", "date"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["link_preview"] + }, + "link_preview": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + }, + "required": ["type", "link_preview"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page"] + }, + "page": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + } + }, + "required": ["type", "page"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["database"] + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + } + }, + "required": ["type", "database"] + } + ] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationResponse" + }, + "plain_text": { + "type": "string" + }, + "href": { + "type": ["string", "null"] + } + }, + "required": ["type", "mention", "annotations", "plain_text", "href"] + }, + "EquationRichTextItemResponse": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["equation"] + }, + "equation": { + "type": "object", + "properties": { + "expression": { + "type": "string" + } + }, + "required": ["expression"] + }, + "annotations": { + "$ref": "#/components/schemas/AnnotationResponse" + }, + "plain_text": { + "type": "string" + }, + "href": { + "type": ["string", "null"] + } + }, + "required": ["type", "equation", "annotations", "plain_text", "href"] + }, + "AnnotationResponse": { + "type": "object", + "properties": { + "bold": { + "type": "boolean" + }, + "italic": { + "type": "boolean" + }, + "strikethrough": { + "type": "boolean" + }, + "underline": { + "type": "boolean" + }, + "code": { + "type": "boolean" + }, + "color": { + "type": "string", + "enum": [ + "default", + "gray", + "brown", + "orange", + "yellow", + "green", + "blue", + "purple", + "pink", + "red", + "gray_background", + "brown_background", + "orange_background", + "yellow_background", + "green_background", + "blue_background", + "purple_background", + "pink_background", + "red_background" + ] + } + }, + "required": [ + "bold", + "italic", + "strikethrough", + "underline", + "code", + "color" + ] + }, + "DateResponse": { + "type": "object", + "properties": { + "start": { + "type": "string" + }, + "end": { + "type": ["string", "null"] + }, + "time_zone": { + "type": ["string", "null"] + } + }, + "required": ["start", "end", "time_zone"] + }, + "PartialUserObjectResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["user"] + }, + "id": { + "type": "string" + } + }, + "required": ["object", "id"] + }, + "DatabasePropertyConfigResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/TitlePropertyResponse" + }, + { + "$ref": "#/components/schemas/RichTextPropertyResponse" + }, + { + "$ref": "#/components/schemas/NumberPropertyResponse" + }, + { + "$ref": "#/components/schemas/SelectPropertyResponse" + }, + { + "$ref": "#/components/schemas/MultiSelectPropertyResponse" + }, + { + "$ref": "#/components/schemas/DatePropertyResponse" + }, + { + "$ref": "#/components/schemas/PeoplePropertyResponse" + }, + { + "$ref": "#/components/schemas/FilePropertyResponse" + }, + { + "$ref": "#/components/schemas/CheckboxPropertyResponse" + }, + { + "$ref": "#/components/schemas/URLPropertyResponse" + }, + { + "$ref": "#/components/schemas/EmailPropertyResponse" + }, + { + "$ref": "#/components/schemas/PhoneNumberPropertyResponse" + }, + { + "$ref": "#/components/schemas/FormulaPropertyResponse" + }, + { + "$ref": "#/components/schemas/RelationPropertyResponse" + }, + { + "$ref": "#/components/schemas/RollupPropertyResponse" + }, + { + "$ref": "#/components/schemas/CreatedTimePropertyResponse" + }, + { + "$ref": "#/components/schemas/CreatedByPropertyResponse" + }, + { + "$ref": "#/components/schemas/LastEditedTimePropertyResponse" + }, + { + "$ref": "#/components/schemas/LastEditedByPropertyResponse" + } + ] + }, + "TitlePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["title"] + }, + "title": { + "type": "object" + } + }, + "required": ["id", "type", "title"] + }, + "RichTextPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["rich_text"] + }, + "rich_text": { + "type": "object" + } + }, + "required": ["id", "type", "rich_text"] + }, + "NumberPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["number"] + }, + "number": { + "type": "object", + "properties": { + "format": { + "type": "string" + } + }, + "required": ["format"] + } + }, + "required": ["id", "type", "number"] + }, + "SelectPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["select"] + }, + "select": { + "type": "object", + "properties": { + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SelectOption" + } + } + }, + "required": ["options"] + } + }, + "required": ["id", "type", "select"] + }, + "MultiSelectPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["multi_select"] + }, + "multi_select": { + "type": "object", + "properties": { + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SelectOption" + } + } + }, + "required": ["options"] + } + }, + "required": ["id", "type", "multi_select"] + }, + "DatePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["date"] + }, + "date": { + "type": "object" + } + }, + "required": ["id", "type", "date"] + }, + "PeoplePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["people"] + }, + "people": { + "type": "object" + } + }, + "required": ["id", "type", "people"] + }, + "FilePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["files"] + }, + "files": { + "type": "object" + } + }, + "required": ["id", "type", "files"] + }, + "CheckboxPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["checkbox"] + }, + "checkbox": { + "type": "object" + } + }, + "required": ["id", "type", "checkbox"] + }, + "URLPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["url"] + }, + "url": { + "type": "object" + } + }, + "required": ["id", "type", "url"] + }, + "EmailPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["email"] + }, + "email": { + "type": "object" + } + }, + "required": ["id", "type", "email"] + }, + "PhoneNumberPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["phone_number"] + }, + "phone_number": { + "type": "object" + } + }, + "required": ["id", "type", "phone_number"] + }, + "FormulaPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["formula"] + }, + "formula": { + "type": "object", + "properties": { + "expression": { + "type": "string" + } + }, + "required": ["expression"] + } + }, + "required": ["id", "type", "formula"] + }, + "RelationPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["relation"] + }, + "relation": { + "type": "object", + "properties": { + "database_id": { + "type": "string" + }, + "synced_property_name": { + "type": "string" + }, + "synced_property_id": { + "type": "string" + } + }, + "required": [ + "database_id", + "synced_property_name", + "synced_property_id" + ] + } + }, + "required": ["id", "type", "relation"] + }, + "RollupPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["rollup"] + }, + "rollup": { + "type": "object", + "properties": { + "relation_property_name": { + "type": "string" + }, + "relation_property_id": { + "type": "string" + }, + "rollup_property_name": { + "type": "string" + }, + "rollup_property_id": { + "type": "string" + }, + "function": { + "type": "string" + } + }, + "required": [ + "relation_property_name", + "relation_property_id", + "rollup_property_name", + + "rollup_property_id", + "function" + ] + } + }, + "required": ["id", "type", "rollup"] + }, + "CreatedTimePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["created_time"] + }, + "created_time": { + "type": "object" + } + }, + "required": ["id", "type", "created_time"] + }, + "CreatedByPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["created_by"] + }, + "created_by": { + "type": "object" + } + }, + "required": ["id", "type", "created_by"] + }, + "LastEditedTimePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["last_edited_time"] + }, + "last_edited_time": { + "type": "object" + } + }, + "required": ["id", "type", "last_edited_time"] + }, + "LastEditedByPropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["last_edited_by"] + }, + "last_edited_by": { + "type": "object" + } + }, + "required": ["id", "type", "last_edited_by"] + }, + "SelectOption": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string" + } + }, + "required": ["id", "name", "color"] + }, + "PropertyUpdateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "PropertySchema": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": ["string", "null"] + } + }, + "required": ["type"] + }, + "PropertyFilter": { + "oneOf": [ + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "title": { + "$ref": "#/components/schemas/TextPropertyFilter" + } + }, + "required": ["property", "title"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "rich_text": { + "$ref": "#/components/schemas/TextPropertyFilter" + } + }, + "required": ["property", "rich_text"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "number": { + "$ref": "#/components/schemas/NumberPropertyFilter" + } + }, + "required": ["property", "number"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "checkbox": { + "$ref": "#/components/schemas/CheckboxPropertyFilter" + } + }, + "required": ["property", "checkbox"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "select": { + "$ref": "#/components/schemas/SelectPropertyFilter" + } + }, + "required": ["property", "select"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "multi_select": { + "$ref": "#/components/schemas/MultiSelectPropertyFilter" + } + }, + "required": ["property", "multi_select"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "date": { + "$ref": "#/components/schemas/DatePropertyFilter" + } + }, + "required": ["property", "date"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "people": { + "$ref": "#/components/schemas/PeoplePropertyFilter" + } + }, + "required": ["property", "people"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "files": { + "$ref": "#/components/schemas/FilesPropertyFilter" + } + }, + "required": ["property", "files"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "url": { + "$ref": "#/components/schemas/TextPropertyFilter" + } + }, + "required": ["property", "url"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "email": { + "$ref": "#/components/schemas/TextPropertyFilter" + } + }, + "required": ["property", "email"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "phone_number": { + "$ref": "#/components/schemas/TextPropertyFilter" + } + }, + "required": ["property", "phone_number"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "relation": { + "$ref": "#/components/schemas/RelationPropertyFilter" + } + }, + "required": ["property", "relation"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "created_by": { + "$ref": "#/components/schemas/PeoplePropertyFilter" + } + }, + "required": ["property", "created_by"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "created_time": { + "$ref": "#/components/schemas/DatePropertyFilter" + } + }, + "required": ["property", "created_time"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "last_edited_by": { + "$ref": "#/components/schemas/PeoplePropertyFilter" + } + }, + "required": ["property", "last_edited_by"] + }, + { + "type": "object", + "properties": { + "property": { + "type": "string" + }, + "last_edited_time": { + "$ref": "#/components/schemas/DatePropertyFilter" + } + }, + "required": ["property", "last_edited_time"] + }, + { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "enum": ["created_time", "last_edited_time"] + }, + "created_time": { + "$ref": "#/components/schemas/DatePropertyFilter" + } + }, + "required": ["timestamp", "created_time"] + }, + { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "enum": ["created_time", "last_edited_time"] + }, + "last_edited_time": { + "$ref": "#/components/schemas/DatePropertyFilter" + } + }, + "required": ["timestamp", "last_edited_time"] + } + ] + }, + "TextPropertyFilter": { + "type": "object", + "properties": { + "equals": { + "type": "string" + }, + "does_not_equal": { + "type": "string" + }, + "contains": { + "type": "string" + }, + "does_not_contain": { + "type": "string" + }, + "starts_with": { + "type": "string" + }, + "ends_with": { + "type": "string" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "NumberPropertyFilter": { + "type": "object", + "properties": { + "equals": { + "type": "number" + }, + "does_not_equal": { + "type": "number" + }, + "greater_than": { + "type": "number" + }, + "less_than": { + "type": "number" + }, + "greater_than_or_equal_to": { + "type": "number" + }, + "less_than_or_equal_to": { + "type": "number" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "CheckboxPropertyFilter": { + "type": "object", + "properties": { + "equals": { + "type": "boolean" + }, + "does_not_equal": { + "type": "boolean" + } + } + }, + "SelectPropertyFilter": { + "type": "object", + "properties": { + "equals": { + "type": "string" + }, + "does_not_equal": { + "type": "string" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "MultiSelectPropertyFilter": { + "type": "object", + "properties": { + "contains": { + "type": "string" + }, + "does_not_contain": { + "type": "string" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "DatePropertyFilter": { + "type": "object", + "properties": { + "equals": { + "type": "string" + }, + "before": { + "type": "string" + }, + "after": { + "type": "string" + }, + "on_or_before": { + "type": "string" + }, + "on_or_after": { + "type": "string" + }, + "past_week": {}, + "past_month": {}, + "past_year": {}, + "next_week": {}, + "next_month": {}, + "next_year": {}, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "PeoplePropertyFilter": { + "type": "object", + "properties": { + "contains": { + "type": "string" + }, + "does_not_contain": { + "type": "string" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "FilesPropertyFilter": { + "type": "object", + "properties": { + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "RelationPropertyFilter": { + "type": "object", + "properties": { + "contains": { + "type": "string" + }, + "does_not_contain": { + "type": "string" + }, + "is_empty": { + "type": "boolean" + }, + "is_not_empty": { + "type": "boolean" + } + } + }, + "CompoundFilter": { + "type": "object", + "properties": { + "and": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyFilter" + } + }, + "or": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyFilter" + } + } + } + }, + "BlockObjectRequest": { + "oneOf": [ + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["paragraph"] + }, + "paragraph": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "paragraph"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["heading_1"] + }, + "heading_1": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "heading_1"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["heading_2"] + }, + "heading_2": { + "type": "object", + + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "heading_2"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["heading_3"] + }, + "heading_3": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "heading_3"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["bulleted_list_item"] + }, + "bulleted_list_item": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "bulleted_list_item"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["numbered_list_item"] + }, + "numbered_list_item": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "numbered_list_item"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["to_do"] + }, + "to_do": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "checked": { + "type": "boolean" + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text", "checked"] + } + }, + "required": ["object", "type", "to_do"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["toggle"] + }, + "toggle": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "color": { + "type": "string" + } + }, + "required": ["rich_text"] + } + }, + "required": ["object", "type", "toggle"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["code"] + }, + "code": { + "type": "object", + "properties": { + "rich_text": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + }, + "language": { + "type": "string" + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["rich_text", "language"] + } + }, + "required": ["object", "type", "code"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["child_page"] + }, + "child_page": { + "type": "object", + "properties": { + "title": { + "type": "string" + } + }, + "required": ["title"] + } + }, + "required": ["object", "type", "child_page"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["child_database"] + }, + "child_database": { + "type": "object", + "properties": { + "title": { + "type": "string" + } + }, + "required": ["title"] + } + }, + "required": ["object", "type", "child_database"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["embed"] + }, + "embed": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["url"] + } + }, + "required": ["object", "type", "embed"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["image"] + }, + "image": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["external"] + } + }, + "required": ["object", "type", "image"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["video"] + }, + "video": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["external"] + } + }, + "required": ["object", "type", "video"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["file"] + }, + "file": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["external"] + } + }, + "required": ["object", "type", "file"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["pdf"] + }, + "pdf": { + "type": "object", + "properties": { + "external": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["external"] + } + }, + "required": ["object", "type", "pdf"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["bookmark"] + }, + "bookmark": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "caption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + }, + "required": ["url"] + } + }, + "required": ["object", "type", "bookmark"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["equation"] + }, + "equation": { + "type": "object", + "properties": { + "expression": { + "type": "string" + } + }, + "required": ["expression"] + } + }, + "required": ["object", "type", "equation"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["divider"] + }, + "divider": { + "type": "object" + } + }, + "required": ["object", "type", "divider"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["table_of_contents"] + }, + "table_of_contents": { + "type": "object", + "properties": { + "color": { + "type": "string" + } + } + } + }, + "required": ["object", "type", "table_of_contents"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["column_list"] + }, + "column_list": { + "type": "object" + } + }, + "required": ["object", "type", "column_list"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["column"] + }, + "column": { + "type": "object" + } + }, + "required": ["object", "type", "column"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["link_to_page"] + }, + "link_to_page": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["page_id"] + }, + "page_id": { + "type": "string" + } + }, + "required": ["type", "page_id"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["database_id"] + }, + "database_id": { + "type": "string" + } + }, + "required": ["type", "database_id"] + } + ] + } + }, + "required": ["object", "type", "link_to_page"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["table"] + }, + "table": { + "type": "object", + "properties": { + "table_width": { + "type": "integer" + }, + "has_column_header": { + "type": "boolean" + }, + "has_row_header": { + "type": "boolean" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BlockObjectRequest" + } + } + }, + "required": ["table_width", "children"] + } + }, + "required": ["object", "type", "table"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["table_row"] + }, + "table_row": { + "type": "object", + "properties": { + "cells": { + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RichTextItemRequest" + } + } + } + }, + "required": ["cells"] + } + }, + "required": ["object", "type", "table_row"] + }, + { + "type": "object", + "properties": { + "object": { + "type": "string", + "enum": ["block"] + }, + "type": { + "type": "string", + "enum": ["synced_block"] + }, + "synced_block": { + "type": "object", + "properties": { + "synced_from": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["block_id"] + }, + "block_id": { + "type": "string" + } + }, + "required": ["type", "block_id"] + }, + { + "type": "null" + } + ] + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BlockObjectRequest" + } + } + } + } + }, + "required": ["object", "type", "synced_block"] + } + ] + } + } + } +} diff --git a/packages/openapi-to-ts-client/fixtures/openapi/pet-store.json b/packages/openapi-to-ts-client/fixtures/openapi/pet-store.json new file mode 100644 index 0000000..9d717b6 --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/openapi/pet-store.json @@ -0,0 +1,177 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "tags": ["pets"], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "A paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "tags": ["pets"], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "summary": "Info for a specific pet", + "operationId": "showPetById", + "tags": ["pets"], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to retrieve", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "maxItems": 100, + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "Error": { + "type": "object", + "required": ["code", "message"], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} diff --git a/packages/openapi-to-ts-client/fixtures/openapi/tic-tac-toe.json b/packages/openapi-to-ts-client/fixtures/openapi/tic-tac-toe.json new file mode 100644 index 0000000..79f34cb --- /dev/null +++ b/packages/openapi-to-ts-client/fixtures/openapi/tic-tac-toe.json @@ -0,0 +1,261 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Tic Tac Toe", + "description": "This API allows writing down marks on a Tic Tac Toe board\nand requesting the state of the board or of individual squares.\n", + "version": "1.0.0" + }, + "tags": [ + { + "name": "Gameplay" + } + ], + "paths": { + "/board": { + "get": { + "summary": "Get the whole board", + "description": "Retrieves the current state of the board and the winner.", + "tags": ["Gameplay"], + "operationId": "get-board", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status" + } + } + } + } + }, + "security": [ + { + "defaultApiKey": [] + }, + { + "app2AppOauth": ["board:read"] + } + ] + } + }, + "/board/{row}/{column}": { + "parameters": [ + { + "$ref": "#/components/parameters/rowParam" + }, + { + "$ref": "#/components/parameters/columnParam" + } + ], + "get": { + "summary": "Get a single board square", + "description": "Retrieves the requested square.", + "tags": ["Gameplay"], + "operationId": "get-square", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mark" + } + } + } + }, + "400": { + "description": "The provided parameters are incorrect", + "content": { + "text/html": { + "schema": { + "$ref": "#/components/schemas/errorMessage" + }, + "example": "Illegal coordinates" + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "user2AppOauth": ["board:read"] + } + ] + }, + "put": { + "summary": "Set a single board square", + "description": "Places a mark on the board and retrieves the whole board and the winner (if any).", + "tags": ["Gameplay"], + "operationId": "put-square", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mark" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status" + } + } + } + }, + "400": { + "description": "The provided parameters are incorrect", + "content": { + "text/html": { + "schema": { + "$ref": "#/components/schemas/errorMessage" + }, + "examples": { + "illegalCoordinates": { + "value": "Illegal coordinates." + }, + "notEmpty": { + "value": "Square is not empty." + }, + "invalidMark": { + "value": "Invalid Mark (X or O)." + } + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "user2AppOauth": ["board:write"] + } + ] + } + } + }, + "components": { + "parameters": { + "rowParam": { + "description": "Board row (vertical coordinate)", + "name": "row", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/coordinate" + } + }, + "columnParam": { + "description": "Board column (horizontal coordinate)", + "name": "column", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/coordinate" + } + } + }, + "schemas": { + "errorMessage": { + "type": "string", + "maxLength": 256, + "description": "A text message describing an error" + }, + "coordinate": { + "type": "integer", + "minimum": 1, + "maximum": 3, + "example": 1 + }, + "mark": { + "type": "string", + "enum": [".", "X", "O"], + "description": "Possible values for a board square. `.` means empty square.", + "example": "." + }, + "board": { + "type": "array", + "maxItems": 3, + "minItems": 3, + "items": { + "type": "array", + "maxItems": 3, + "minItems": 3, + "items": { + "$ref": "#/components/schemas/mark" + } + } + }, + "winner": { + "type": "string", + "enum": [".", "X", "O"], + "description": "Winner of the game. `.` means nobody has won yet.", + "example": "." + }, + "status": { + "type": "object", + "properties": { + "winner": { + "$ref": "#/components/schemas/winner" + }, + "board": { + "$ref": "#/components/schemas/board" + } + } + } + }, + "securitySchemes": { + "defaultApiKey": { + "description": "API key provided in console", + "type": "apiKey", + "name": "api-key", + "in": "header" + }, + "basicHttpAuthentication": { + "description": "Basic HTTP Authentication", + "type": "http", + "scheme": "Basic" + }, + "bearerHttpAuthentication": { + "description": "Bearer token using a JWT", + "type": "http", + "scheme": "Bearer", + "bearerFormat": "JWT" + }, + "app2AppOauth": { + "type": "oauth2", + "flows": { + "clientCredentials": { + "tokenUrl": "https://learn.openapis.org/oauth/2.0/token", + "scopes": { + "board:read": "Read the board" + } + } + } + }, + "user2AppOauth": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://learn.openapis.org/oauth/2.0/auth", + "tokenUrl": "https://learn.openapis.org/oauth/2.0/token", + "scopes": { + "board:read": "Read the board", + "board:write": "Write to the board" + } + } + } + } + } + } +} diff --git a/packages/openapi-to-ts-client/package.json b/packages/openapi-to-ts-client/package.json new file mode 100644 index 0000000..6e423b5 --- /dev/null +++ b/packages/openapi-to-ts-client/package.json @@ -0,0 +1,51 @@ +{ + "name": "@agentic/openapi-to-ts-client", + "version": "0.1.0", + "description": "TODO", + "author": "Travis Fischer ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/transitive-bullshit/agentic.git" + }, + "type": "module", + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "clean": "del dist", + "test": "run-s test:*", + "test:lint": "eslint .", + "test:typecheck": "tsc --noEmit", + "test:unit": "vitest run" + }, + "dependencies": { + "@agentic/core": "workspace:*", + "@apidevtools/swagger-parser": "^10.1.1", + "camelcase": "^8.0.0", + "decamelize": "^6.0.0", + "execa": "^9.5.2", + "json-schema-to-zod": "^2.6.0", + "openapi-types": "^12.1.3", + "zod": "catalog:" + }, + "devDependencies": { + "@agentic/tsconfig": "workspace:*", + "ky": "catalog:" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/openapi-to-ts-client/readme.md b/packages/openapi-to-ts-client/readme.md new file mode 100644 index 0000000..c27faae --- /dev/null +++ b/packages/openapi-to-ts-client/readme.md @@ -0,0 +1,24 @@ +

+ + Agentic + +

+ +

+ TODO. +

+ +

+ Build Status + NPM + MIT License + Prettier Code Formatting +

+ +# Agentic + +**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.** + +## License + +MIT © [Travis Fischer](https://x.com/transitive_bs) diff --git a/packages/openapi-to-ts-client/src/generate.ts b/packages/openapi-to-ts-client/src/generate.ts new file mode 100644 index 0000000..7de7e93 --- /dev/null +++ b/packages/openapi-to-ts-client/src/generate.ts @@ -0,0 +1,614 @@ +/* eslint-disable no-template-curly-in-string */ +import * as fs from 'node:fs/promises' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import type { IJsonSchema, OpenAPIV3 } from 'openapi-types' +import { assert } from '@agentic/core' +import SwaggerParser from '@apidevtools/swagger-parser' +import camelCase from 'camelcase' +import decamelize from 'decamelize' +import { execa } from 'execa' + +import { convertParametersToJSONSchema } from './openapi-parameters-to-json-schema' +import { + dereference, + dereferenceFull, + getAndResolve, + getComponentName, + getOperationParamsName, + getOperationResponseName, + jsonSchemaToZod, + prettify +} from './utils' + +const dirname = path.dirname(fileURLToPath(import.meta.url)) +const jsonContentType = 'application/json' +const multipartFormData = 'multipart/form-data' + +const httpMethods = [ + 'get', + 'post', + 'put', + 'delete', + 'options', + 'head', + 'patch', + 'trace' +] as const + +async function main() { + const pathToOpenApiSpec = process.argv[2] + assert(pathToOpenApiSpec, 'Missing path to OpenAPI spec') + + const parser = new SwaggerParser() + const spec = (await parser.bundle(pathToOpenApiSpec)) as OpenAPIV3.Document + // | OpenAPIV3_1.Document + + if ( + // TODO: make this less brittle + spec.openapi !== '3.0.0' && + spec.openapi !== '3.1.0' && + spec.openapi !== '3.1.1' + ) { + throw new Error(`Unexpected OpenAPI version "${spec.openapi}"`) + } + + const openapiSpecName = path.basename(pathToOpenApiSpec, '.json') + assert( + openapiSpecName.toLowerCase() === openapiSpecName, + `OpenAPI spec name "${openapiSpecName}" must be in kebab case` + ) + const name = camelCase(openapiSpecName, { pascalCase: true }) + const nameLowerCase = name.toLowerCase() + const nameSnakeCase = decamelize(name) + const nameKebabCase = decamelize(name, { separator: '-' }) + const nameUpperCase = nameSnakeCase.toUpperCase() + const clientName = `${name}Client` + const namespaceName = nameLowerCase + // const destFolder = path.join('packages', nameKebabCase) + // const destFolderSrc = path.join(destFolder, 'src') + + const destFolder = path.join(dirname, '..', 'fixtures', 'generated') + const destFileClient = path.join(destFolder, `${nameKebabCase}-client.ts`) + + const apiBaseUrl = spec.servers?.[0]?.url + + const securitySchemes = spec.components?.securitySchemes + const resolvedSecuritySchemes: Record< + string, + OpenAPIV3.SecuritySchemeObject + > = {} + const apiKeyHeaderNames: string[] = [] + + if (securitySchemes) { + for (const [securitySchemaName, maybeSecuritySchema] of Object.entries( + securitySchemes + )) { + const securitySchema = dereferenceFull(maybeSecuritySchema, parser.$refs) + if (!securitySchema) continue + + resolvedSecuritySchemes[securitySchemaName] = securitySchema + + switch (securitySchema.type) { + case 'apiKey': + apiKeyHeaderNames.push(securitySchemaName) + break + + case 'http': + if (securitySchema.scheme === 'bearer') { + apiKeyHeaderNames.push(securitySchemaName) + } + break + + case 'oauth2': + apiKeyHeaderNames.push(securitySchemaName) + break + + default: + console.log( + 'unsupported security schema', + securitySchemaName, + securitySchema + ) + } + } + } + + const hasGlobalApiKeyInHeader = apiKeyHeaderNames.length === 1 + const componentsToProcess = new Set() + + const requestBodyJSONSchemaPaths = [ + 'requestBody', + 'content', + jsonContentType, + 'schema' + ] + + const requestBodyFormDataJSONSchemaPaths = [ + 'requestBody', + 'content', + multipartFormData, + 'schema' + ] + + const operationResponsePaths = [ + ['responses', '200', 'content', jsonContentType, 'schema'], + ['responses', '201', 'content', jsonContentType, 'schema'] + // ['responses', 'default', 'content', jsonContentType, 'schema'] + ] + + const operationRequestPaths = [ + requestBodyJSONSchemaPaths, + requestBodyFormDataJSONSchemaPaths + ] + + const operationIds = new Set() + const operationSchemas: Record = {} + const componentSchemas: Record = {} + const aiClientMethods: string[] = [] + + for (const path in spec.paths) { + const pathItem = spec.paths[path] + assert(pathItem) + // console.log(JSON.stringify(pathItem, null, 2)) + + for (const method of httpMethods) { + const operation = pathItem[method] + if (!operation) { + continue + } + + if (method === 'trace' || method === 'options') { + continue + } + + const operationName = + // TODO: better camelCase fallback + operation.operationId || `${method}${path.replaceAll(/\W+/g, '_')}` + assert( + operationName, + `Invalid operation name ${operationName} for path "${method} ${path}"` + ) + assert( + !operationIds.has(operationName), + `Duplicate operation name "${operationName}"` + ) + operationIds.add(operationName) + const operationNameSnakeCase = decamelize(operationName) + + // if (path !== '/crawl/status/{jobId}') continue + // if (path !== '/pets' || method !== 'post') continue + // console.log(method, path, operationName) + + const operationParamsJSONSchema = { + type: 'object', + properties: {} as Record, + required: [] as string[], + $refs: [] as string[] + } + + const operationResponseJSONSchemas: Record = {} + + const operationParamsSources: Record = {} + + // eslint-disable-next-line unicorn/consistent-function-scoping + function addJSONSchemaParams(schema: IJsonSchema, source: string) { + dereferenceFull(schema, parser.$refs, componentsToProcess) + + if (schema.$ref) { + operationParamsJSONSchema.$refs.push(schema.$ref) + + const derefed = dereference(schema, parser.$refs, componentsToProcess) + if (derefed?.properties) { + for (const key of Object.keys(derefed.properties)) { + assert( + !operationParamsSources[key], + `Duplicate params key ${key} for operation ${operationName} from ${operationParamsSources[key]} and ${source}` + ) + operationParamsSources[key] = source + } + } + } else { + assert(schema.type === 'object') + + if (schema.properties) { + operationParamsJSONSchema.properties = { + ...operationParamsJSONSchema.properties, + ...schema.properties + } + + for (const key of Object.keys(schema.properties)) { + assert( + !operationParamsSources[key], + `Duplicate params key ${key} for operation ${operationName} from ${operationParamsSources[key]} and ${source}` + ) + operationParamsSources[key] = source + } + } + + if (schema.required) { + operationParamsJSONSchema.required = [ + ...operationParamsJSONSchema.required, + ...schema.required + ] + } + } + } + + // eslint-disable-next-line unicorn/consistent-function-scoping + function addJSONSchemaResponse(schema: IJsonSchema, status: string) { + dereferenceFull(schema, parser.$refs, componentsToProcess) + operationResponseJSONSchemas[status] = schema + } + + for (const schemaPath of operationRequestPaths) { + const res = getAndResolve( + operation, + schemaPath, + parser.$refs, + componentsToProcess + ) + + if (res) { + addJSONSchemaParams( + res, + schemaPath[2] === jsonContentType ? 'body' : 'formData' + ) + break + } + } + + for (const schemaPath of operationResponsePaths) { + const res = getAndResolve( + operation, + schemaPath, + parser.$refs, + componentsToProcess + ) + + if (res) { + const status = schemaPath[1]! + assert( + status, + `Invalid status ${status} for operation ${operationName}` + ) + + addJSONSchemaResponse(res, status) + break + } + } + + if (operation.parameters) { + const params = convertParametersToJSONSchema(operation.parameters) + + if (params.body) { + addJSONSchemaParams(params.body, 'body') + } + + if (params.formData) { + addJSONSchemaParams(params.formData, 'formData') + } + + if (params.headers) { + addJSONSchemaParams(params.headers, 'formData') + } + + if (params.path) { + addJSONSchemaParams(params.path, 'path') + } + + if (params.query) { + addJSONSchemaParams(params.query, 'query') + } + } + + const operationParamsName = getOperationParamsName( + operationName, + componentSchemas + ) + const operationResponseName = getOperationResponseName(operationName) + + { + // Merge all operations params into one schema declaration + let operationsParamsSchema = jsonSchemaToZod( + operationParamsJSONSchema, + { name: `${operationParamsName}Schema`, type: operationParamsName } + ) + + if (operationParamsJSONSchema.$refs.length) { + const refSchemas = operationParamsJSONSchema.$refs.map( + (ref) => `${getComponentName(ref)!}Schema` + ) + + operationsParamsSchema = operationsParamsSchema.replace( + // eslint-disable-next-line security/detect-non-literal-regexp + new RegExp(`(${operationParamsName}Schema = .*)$`, 'm'), + // eslint-disable-next-line unicorn/no-array-reduce + refSchemas.reduce( + (acc, refSchema) => `${acc}.merge(${refSchema})`, + '$1' + ) + ) + } + + assert( + !componentSchemas[operationParamsName], + `Operation params name "${operationParamsName}" conflicts with component name` + ) + operationSchemas[operationParamsName] = operationsParamsSchema + } + + const operationResponseJSONSchema = operationResponseJSONSchemas['200'] + if (operationResponseJSONSchema) { + let isDuplicateDefinition = false + + if (operationResponseJSONSchema.$ref) { + const componentName = getComponentName( + operationResponseJSONSchema.$ref + ) + if (componentName === operationResponseName) { + isDuplicateDefinition = true + } + } + + if (!isDuplicateDefinition) { + const operationResponseSchema = jsonSchemaToZod( + operationResponseJSONSchema, + { + name: `${operationResponseName}Schema`, + type: operationResponseName + } + ) + + assert( + !componentSchemas[operationResponseName], + `Operation response name "${operationResponseName}" conflicts with component name` + ) + operationSchemas[operationResponseName] = operationResponseSchema + } + } else { + assert( + !componentSchemas[operationResponseName], + `Operation response name "${operationResponseName}" conflicts with component name` + ) + operationSchemas[operationResponseName] = + `export type ${operationResponseName} = undefined\n` + } + + // console.log( + // JSON.stringify( + // { + // operationName, + // operationParamsSources, + // operationParamsJSONSchema + // }, + // null, + // 2 + // ) + // ) + + const queryParams = Object.keys(operationParamsSources).filter( + (key) => operationParamsSources[key] === 'query' + ) + const hasQueryParams = queryParams.length > 0 + + const bodyParams = Object.keys(operationParamsSources).filter( + (key) => operationParamsSources[key] === 'body' + ) + const hasBodyParams = bodyParams.length > 0 + + const formDataParams = Object.keys(operationParamsSources).filter( + (key) => operationParamsSources[key] === 'formData' + ) + const hasFormDataParams = formDataParams.length > 0 + + const pathParams = Object.keys(operationParamsSources).filter( + (key) => operationParamsSources[key] === 'path' + ) + const hasPathParams = pathParams.length > 0 + + const headersParams = Object.keys(operationParamsSources).filter( + (key) => operationParamsSources[key] === 'headers' + ) + const hasHeadersParams = headersParams.length > 0 + + const onlyHasOneParamsSource = + new Set(Object.values(operationParamsSources)).size === 1 + const onlyHasPathParams = hasPathParams && onlyHasOneParamsSource + + const pathTemplate = hasPathParams + ? '`' + path.replaceAll(/{([^}]+)}/g, '${params["$1"]}') + '`' + : `'${path}'` + + let description = operation.description || operation.summary + if (description && !/[!.?]$/.test(description)) { + description += '.' + } + + const aiClientMethod = ` + ${description ? `/**\n * ${description}\n */` : ''} + @aiFunction({ + name: '${operationNameSnakeCase}', + ${description ? `description: '${description}',` : ''} + inputSchema: ${namespaceName}.${operationParamsName}Schema, + }) + async ${operationName}(params: ${namespaceName}.${operationParamsName}): Promise<${namespaceName}.${operationResponseName}> { + return this.ky.${method}(${pathTemplate}${ + onlyHasPathParams + ? '' + : `, { + ${hasQueryParams ? (onlyHasOneParamsSource ? `searchParams: sanitizeSearchParams(params),` : `searchParams: sanitizeSearchParams(pick(params, '${queryParams.join("', '")}')),`) : ''} + ${hasBodyParams ? (onlyHasOneParamsSource ? `json: params,` : `json: pick(params, '${bodyParams.join("', '")}'),`) : ''} + ${hasFormDataParams ? (onlyHasOneParamsSource ? `form: params,` : `form: pick(params, '${formDataParams.join("', '")}'),`) : ''} + ${hasHeadersParams ? (onlyHasOneParamsSource ? `headers: params,` : `headers: pick(params, '${headersParams.join("', '")}'),`) : ''} + }` + }).json<${namespaceName}.${operationResponseName}>() + } + ` + + aiClientMethods.push(aiClientMethod) + } + } + + const processedComponents = new Set() + const componentToRefs: Record< + string, + { dereferenced: any; refs: Set } + > = {} + + for (const ref of componentsToProcess) { + const component = parser.$refs.get(ref) + assert(component) + + const resolved = new Set() + const dereferenced = dereference(component, parser.$refs) + dereference(component, parser.$refs, resolved, 0, Number.POSITIVE_INFINITY) + assert(dereferenced) + + for (const ref of resolved) { + assert(componentsToProcess.has(ref)) + } + + componentToRefs[ref] = { dereferenced, refs: resolved } + } + + const sortedComponents = Object.keys(componentToRefs).sort( + (a, b) => componentToRefs[a]!.refs.size - componentToRefs[b]!.refs.size + ) + + for (const ref of sortedComponents) { + const type = getComponentName(ref) + assert(type, `Invalid ref name ${ref}`) + + const name = `${type}Schema` + + const { dereferenced, refs } = componentToRefs[ref]! + if (processedComponents.has(ref)) { + continue + } + + for (const r of refs) { + if (processedComponents.has(r)) { + continue + } + + processedComponents.add(r) + } + + processedComponents.add(ref) + + const schema = jsonSchemaToZod(dereferenced, { name, type }) + componentSchemas[type] = schema + + // console.log(ref, name, dereferenced) + } + + // console.log( + // '\ncomponents', + // Array.from(componentsToProcess) + // .map((ref) => getComponentName(ref)) + // .sort() + // ) + + // console.log( + // '\nmodels', + // Object.fromEntries( + // await Promise.all( + // Object.entries(schemaInput).map(async ([key, value]) => { + // return [key, await prettify(value)] + // }) + // ) + // ) + // ) + + const aiClientMethodsString = aiClientMethods.join('\n\n') + + const header = ` +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { + AIFunctionsProvider, + ${aiClientMethods.length > 0 ? 'aiFunction,' : ''} + ${hasGlobalApiKeyInHeader ? 'assert,' : ''} + ${hasGlobalApiKeyInHeader ? 'getEnv,' : ''} + ${aiClientMethodsString.includes('pick(') ? 'pick,' : ''} + ${aiClientMethodsString.includes('sanitizeSearchParams(') ? 'sanitizeSearchParams,' : ''} +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod'`.trim() + + const outputTypes = ( + await prettify( + [ + header, + `export namespace ${namespaceName} {`, + apiBaseUrl ? `export const apiBaseUrl = '${apiBaseUrl}'` : undefined, + ...Object.values(componentSchemas), + ...Object.values(operationSchemas), + '}' + ] + .filter(Boolean) + .join('\n\n') + ) + ) + .replaceAll(/z\.object\({}\)\.merge\(([^)]*)\)/g, '$1') + .replaceAll(/\/\*\*(\S.*)\*\//g, '/** $1 */') + + const output = await prettify( + [ + outputTypes, + ` +export class ${clientName} extends AIFunctionsProvider { + protected readonly ky: KyInstance + ${hasGlobalApiKeyInHeader ? 'protected readonly apiKey: string' : ''} + protected readonly apiBaseUrl: string + + constructor({ + ${hasGlobalApiKeyInHeader ? `apiKey = getEnv('${nameUpperCase}_API_KEY'),` : ''} + apiBaseUrl${apiBaseUrl ? ` = ${namespaceName}.apiBaseUrl` : ''}, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + ${ + hasGlobalApiKeyInHeader + ? `assert( + apiKey, + '${clientName} missing required "apiKey" (defaults to "${nameUpperCase}_API_KEY")' + )` + : '' + } + super() + + ${hasGlobalApiKeyInHeader ? `this.apiKey = apiKey` : ''} + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl, + ${ + hasGlobalApiKeyInHeader + ? `headers: { + ${apiKeyHeaderNames.map((name) => `'${(resolvedSecuritySchemes[name] as any).name || 'Authorization'}': ${(resolvedSecuritySchemes[name] as any).schema?.toLowerCase() === 'bearer' ? '`Bearer ${apiKey}`' : 'apiKey'}`).join(',\n')} + },` + : '' + } + }) + } +`, + aiClientMethodsString, + '}' + ].join('\n\n') + ) + + // console.log(output) + await fs.mkdir(destFolder, { recursive: true }) + await fs.writeFile(destFileClient, output) + await execa('npx', ['eslint', '--fix', destFileClient]) +} + +await main() diff --git a/packages/openapi-to-ts-client/src/index.ts b/packages/openapi-to-ts-client/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/openapi-to-ts-client/src/openapi-parameters-to-json-schema.ts b/packages/openapi-to-ts-client/src/openapi-parameters-to-json-schema.ts new file mode 100644 index 0000000..3f5e753 --- /dev/null +++ b/packages/openapi-to-ts-client/src/openapi-parameters-to-json-schema.ts @@ -0,0 +1,221 @@ +/** + * This file is forked from: https://github.com/kogosoftwarellc/open-api/tree/main/packages/openapi-jsonschema-parameters + * + * The original code is licensed under the MIT license. + */ + +import { type IJsonSchema, type OpenAPI } from 'openapi-types' + +export interface OpenAPIParametersAsJSONSchema { + body?: IJsonSchema + formData?: IJsonSchema + headers?: IJsonSchema + path?: IJsonSchema + query?: IJsonSchema + cookie?: IJsonSchema +} + +const VALIDATION_KEYWORDS = new Set([ + 'additionalItems', + 'default', + 'example', + 'description', + 'enum', + 'examples', + 'exclusiveMaximum', + 'exclusiveMinimum', + 'format', + 'items', + 'maxItems', + 'maxLength', + 'maximum', + 'minItems', + 'minLength', + 'minimum', + 'multipleOf', + 'pattern', + 'title', + 'type', + 'uniqueItems' +]) + +const SUBSCHEMA_KEYWORDS = [ + 'additionalItems', + 'items', + 'contains', + 'additionalProperties', + 'propertyNames', + 'not' +] + +const SUBSCHEMA_ARRAY_KEYWORDS = ['items', 'allOf', 'anyOf', 'oneOf'] + +const SUBSCHEMA_OBJECT_KEYWORDS = [ + 'definitions', + 'properties', + 'patternProperties', + 'dependencies' +] + +export function convertParametersToJSONSchema( + parameters: OpenAPI.Parameters +): OpenAPIParametersAsJSONSchema { + const parametersSchema: OpenAPIParametersAsJSONSchema = {} + const bodySchema = getBodySchema(parameters) + const formDataSchema = getSchema(parameters, 'formData') + const headerSchema = getSchema(parameters, 'header') + const pathSchema = getSchema(parameters, 'path') + const querySchema = getSchema(parameters, 'query') + const cookieSchema = getSchema(parameters, 'cookie') + + if (bodySchema) { + parametersSchema.body = bodySchema + } + + if (formDataSchema) { + parametersSchema.formData = formDataSchema + } + + if (headerSchema) { + parametersSchema.headers = headerSchema + } + + if (pathSchema) { + parametersSchema.path = pathSchema + } + + if (querySchema) { + parametersSchema.query = querySchema + } + + if (cookieSchema) { + parametersSchema.cookie = cookieSchema + } + + return parametersSchema +} + +function copyValidationKeywords(src: any) { + const dst: any = {} + for (let i = 0, keys = Object.keys(src), len = keys.length; i < len; i++) { + const keyword = keys[i] + if (!keyword) continue + + if (VALIDATION_KEYWORDS.has(keyword) || keyword.slice(0, 2) === 'x-') { + dst[keyword] = src[keyword] + } + } + return dst +} + +function handleNullable(schema: IJsonSchema) { + return { anyOf: [schema, { type: 'null' }] } +} + +function handleNullableSchema(schema: any) { + if (typeof schema !== 'object' || schema === null) { + return schema + } + + const newSchema = { ...schema } + + for (const keyword of SUBSCHEMA_KEYWORDS) { + if ( + typeof schema[keyword] === 'object' && + schema[keyword] !== null && + !Array.isArray(schema[keyword]) + ) { + newSchema[keyword] = handleNullableSchema(schema[keyword]) + } + } + + for (const keyword of SUBSCHEMA_ARRAY_KEYWORDS) { + if (Array.isArray(schema[keyword])) { + newSchema[keyword] = schema[keyword].map(handleNullableSchema) + } + } + + for (const keyword of SUBSCHEMA_OBJECT_KEYWORDS) { + if (typeof schema[keyword] === 'object' && schema[keyword] !== null) { + newSchema[keyword] = { ...schema[keyword] } + for (const prop of Object.keys(schema[keyword])) { + newSchema[keyword][prop] = handleNullableSchema(schema[keyword][prop]) + } + } + } + + delete newSchema.$ref + + if (schema.nullable) { + delete newSchema.nullable + return handleNullable(newSchema) + } + return newSchema +} + +function getBodySchema(parameters: any[]) { + let bodySchema = parameters.find((param) => { + return param.in === 'body' && param.schema + }) + + if (bodySchema) { + bodySchema = bodySchema.schema + } + + return bodySchema +} + +function getSchema(parameters: any[], type: string) { + const params = parameters.filter(byIn(type)) + let schema: any + + if (params.length) { + schema = { type: 'object', properties: {} } + + for (const param of params) { + let paramSchema = copyValidationKeywords(param) + + if ('schema' in param) { + paramSchema = { + ...paramSchema, + ...handleNullableSchema(param.schema) + } + if ('examples' in param) { + paramSchema.examples = getExamples(param.examples) + } + schema.properties[param.name] = paramSchema + } else { + if ('examples' in paramSchema) { + paramSchema.examples = getExamples(paramSchema.examples) + } + schema.properties[param.name] = param.nullable + ? handleNullable(paramSchema) + : paramSchema + } + } + + schema.required = getRequiredParams(params) + } + + return schema +} + +function getRequiredParams(parameters: any[]) { + return parameters.filter(byRequired).map(toName) +} + +function getExamples(exampleSchema: any) { + return Object.keys(exampleSchema).map((k) => exampleSchema[k].value) +} + +function byIn(str: string) { + return (param: any) => param.in === str && param.type !== 'file' +} + +function byRequired(param: any) { + return !!param.required +} + +function toName(param: any) { + return param.name +} diff --git a/packages/openapi-to-ts-client/src/utils.ts b/packages/openapi-to-ts-client/src/utils.ts new file mode 100644 index 0000000..aba0364 --- /dev/null +++ b/packages/openapi-to-ts-client/src/utils.ts @@ -0,0 +1,253 @@ +import type SwaggerParser from '@apidevtools/swagger-parser' +import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types' +import { assert } from '@agentic/core' +import { + type JsonSchema, + jsonSchemaToZod as jsonSchemaToZodImpl +} from 'json-schema-to-zod' +import * as prettier from 'prettier' + +export function prettify(source: string): Promise { + return prettier.format(source, { + parser: 'typescript', + semi: false, + singleQuote: true, + jsxSingleQuote: true, + bracketSpacing: true, + bracketSameLine: false, + arrowParens: 'always', + trailingComma: 'none' + }) +} + +export function titleCase(identifier: string): string { + return `${identifier.slice(0, 1).toUpperCase()}${identifier.slice(1)}` +} + +export function unTitleCase(identifier: string): string { + return `${identifier.slice(0, 1).toLowerCase()}${identifier.slice(1)}` +} + +export function getAndResolve( + obj: any, + keys: string[], + refs: SwaggerParser.$Refs, + resolved?: Set, + depth = 0, + maxDepth = 0 +): T | null { + if (obj === undefined) return null + if (typeof obj !== 'object') return null + + if (!keys.length) { + return dereference(obj, refs, resolved, depth, maxDepth) as T + } + + if (obj.$ref) { + const derefed = refs.get(obj.$ref) + resolved?.add(obj.$ref) + if (!derefed) { + return null + } + obj = derefed + } + + const key = keys[0]! + const value = obj[key] + keys = keys.slice(1) + if (value === undefined) { + return null + } + + return getAndResolve(value, keys, refs, resolved, depth, maxDepth) +} + +export function dereferenceFull( + obj: T, + refs: SwaggerParser.$Refs, + resolved?: Set +): Exclude { + return dereference(obj, refs, resolved, 0, Number.POSITIVE_INFINITY) as any +} + +export function dereference( + obj: T, + refs: SwaggerParser.$Refs, + resolved?: Set, + depth = 0, + maxDepth = 1 +): T { + if (!obj) return obj + + if (depth >= maxDepth) { + return obj + } + + if (Array.isArray(obj)) { + return obj.map((item) => + dereference(item, refs, resolved, depth + 1, maxDepth) + ) as T + } else if (typeof obj === 'object') { + if ('$ref' in obj) { + const ref = obj.$ref as string + const derefed = refs.get(ref) + if (!derefed) { + return obj + } + resolved?.add(ref) + derefed.title = ref.split('/').pop()! + return dereference(derefed, refs, resolved, depth + 1, maxDepth) + } else { + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [ + key, + dereference(value, refs, resolved, depth + 1, maxDepth) + ]) + ) as T + } + } else { + return obj + } +} + +export function jsonSchemaToZod( + schema: JsonSchema, + { + name, + type + }: { + name?: string + type?: string + } = {} +): string { + return jsonSchemaToZodImpl(schema, { + name, + module: 'esm', + withJsdocs: true, + type: type ?? true, + noImport: true, + parserOverride: (schema, _refs) => { + if ('$ref' in schema) { + const ref = schema.$ref as string + if (!ref) return + + const name = getComponentName(ref) + if (!name) return + + return `${name}Schema` + } + } + }) +} + +const reservedWords = new Set([ + 'Error', + 'Class', + 'Object', + 'interface', + 'type', + 'default', + 'switch', + 'for', + 'break', + 'case', + 'if', + 'else', + 'while', + 'do', + 'for', + 'return', + 'continue', + 'function', + 'console', + 'window', + 'global', + 'import', + 'export', + 'namespace', + 'using', + 'require', + 'module', + 'process', + 'async', + 'await', + 'const', + 'let', + 'var', + 'void', + 'undefined', + 'abstract', + 'extends', + 'implements', + 'private', + 'protected', + 'public', + 'Infinity', + 'Nan', + 'Math', + 'Date', + 'RegExp', + 'JSON', + 'Buffer', + 'Promise', + 'Symbol', + 'Map', + 'Set', + 'WeakMap', + 'WeakSet', + 'Array', + 'Object', + 'Boolean', + 'Number', + 'String', + 'Function', + 'Symbol' +]) + +export function getComponentName(ref: string) { + const name0 = ref.split('/').pop()! + assert(name0, `Invalid ref name ${ref}`) + + const name1 = titleCase(name0) + assert(name1, `Invalid ref name ${ref}`) + + if (reservedWords.has(name1)) { + return `${name1}Type` + } + + return name1 +} + +export function getOperationParamsName( + operationName: string, + schemas?: Record +) { + const name = `${titleCase(operationName)}Params` + if (!schemas) return name + + let tempName = name + let index = 2 + while (schemas[tempName]) { + tempName = `${name}${index}` + ++index + } + + return tempName +} + +export function getOperationResponseName( + operationName: string, + schemas?: Record +) { + const name = `${titleCase(operationName)}Response` + if (!schemas) return name + + let tempName = name + let index = 2 + while (schemas[tempName]) { + tempName = `${name}${index}` + ++index + } + + return tempName +} diff --git a/packages/openapi-to-ts-client/tsconfig.json b/packages/openapi-to-ts-client/tsconfig.json new file mode 100644 index 0000000..6c8d720 --- /dev/null +++ b/packages/openapi-to-ts-client/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@agentic/tsconfig/base.json", + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2fdfa15..a8ca456 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,7 +160,7 @@ importers: version: 5.8.2 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) zod: specifier: 'catalog:' version: 3.24.2 @@ -814,6 +814,40 @@ importers: specifier: workspace:* version: link:../tsconfig + packages/openapi-to-ts-client: + dependencies: + '@agentic/core': + specifier: workspace:* + version: link:../core + '@apidevtools/swagger-parser': + specifier: ^10.1.1 + version: 10.1.1(openapi-types@12.1.3) + camelcase: + specifier: ^8.0.0 + version: 8.0.0 + decamelize: + specifier: ^6.0.0 + version: 6.0.0 + execa: + specifier: ^9.5.2 + version: 9.5.2 + json-schema-to-zod: + specifier: ^2.6.0 + version: 2.6.0 + openapi-types: + specifier: ^12.1.3 + version: 12.1.3 + zod: + specifier: 'catalog:' + version: 3.24.2 + devDependencies: + '@agentic/tsconfig': + specifier: workspace:* + version: link:../tsconfig + ky: + specifier: 'catalog:' + version: 1.7.5 + packages/people-data-labs: dependencies: '@agentic/core': @@ -1374,6 +1408,22 @@ packages: resolution: {integrity: sha512-IQD9wkVReKAhsEAbDjh/0KrBGTEXelqZLpOBRDaIRvlzZ9sjmUP+gKbpvzyJnei2JHQiE8JAgj7YcNloINbGBw==} engines: {node: '>= 10'} + '@apidevtools/json-schema-ref-parser@11.7.2': + resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==} + engines: {node: '>= 16'} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.1.1': + resolution: {integrity: sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==} + peerDependencies: + openapi-types: '>=7' + '@aws-crypto/crc32@3.0.0': resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} @@ -1935,6 +1985,9 @@ packages: '@js-sdsl/ordered-map@4.4.2': resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@langchain/core@0.3.42': resolution: {integrity: sha512-pT/jC5lqWK3YGDq8dQwgKoa6anqAhMtG1x5JbnrOj9NdaLeBbCKBDQ+/Ykzk3nZ8o+0UMsaXNZo7IVL83VVjHg==} engines: {node: '>=18'} @@ -2972,6 +3025,9 @@ packages: '@rushstack/eslint-patch@1.10.4': resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -2979,6 +3035,10 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@smithy/abort-controller@4.0.1': resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==} engines: {node: '>=18.0.0'} @@ -3202,6 +3262,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} @@ -3227,6 +3290,9 @@ packages: '@types/memcached@2.2.10': resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} @@ -3441,6 +3507,14 @@ packages: zod: optional: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -3653,6 +3727,9 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -3661,6 +3738,10 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + caniuse-lite@1.0.30001678: resolution: {integrity: sha512-RR+4U/05gNtps58PEBDZcPWTgEO2MBeoPZ96aQcjmfkBWRIDfN451fW2qyDA9/+HohLLIL5GqiMwA+IB1pWarw==} @@ -3854,6 +3935,10 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decamelize@6.0.0: + resolution: {integrity: sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + decimal.js@10.5.0: resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} @@ -4275,6 +4360,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.5.2: + resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} + engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.0: resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} engines: {node: '>=12.0.0'} @@ -4344,6 +4433,10 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -4490,6 +4583,10 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -4621,6 +4718,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@8.0.0: + resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} + engines: {node: '>=18.18.0'} + humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -4805,6 +4906,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -4817,6 +4922,10 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -5031,6 +5140,7 @@ packages: libsql@0.4.7: resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lilconfig@3.1.3: @@ -5281,6 +5391,10 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nypm@0.6.0: resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} engines: {node: ^14.16.0 || >=16.10.0} @@ -5456,6 +5570,10 @@ packages: resolution: {integrity: sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==} engines: {node: '>=18'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parseley@0.12.1: resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} @@ -5622,6 +5740,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + process-warning@4.0.1: resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} @@ -6046,6 +6168,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -6342,6 +6468,10 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + universal-github-app-jwt@2.2.0: resolution: {integrity: sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==} @@ -6608,6 +6738,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + zod-to-json-schema@3.24.3: resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==} peerDependencies: @@ -6679,6 +6813,27 @@ snapshots: '@anush008/tokenizers-linux-x64-gnu': 0.0.0 '@anush008/tokenizers-win32-x64-msvc': 0.0.0 + '@apidevtools/json-schema-ref-parser@11.7.2': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + js-yaml: 4.1.0 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.1.1(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 11.7.2 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.17.1 + ajv-draft-04: 1.0.0(ajv@8.17.1) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 @@ -7482,6 +7637,8 @@ snapshots: '@js-sdsl/ordered-map@4.4.2': {} + '@jsdevtools/ono@7.1.3': {} + '@langchain/core@0.3.42(openai@4.87.3(encoding@0.1.13)(ws@8.18.0)(zod@3.24.2))': dependencies: '@cfworker/json-schema': 4.1.1 @@ -8797,6 +8954,8 @@ snapshots: '@rushstack/eslint-patch@1.10.4': {} + '@sec-ant/readable-stream@0.4.1': {} + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -8804,6 +8963,8 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@smithy/abort-controller@4.0.1': dependencies: '@smithy/types': 4.1.0 @@ -9142,6 +9303,11 @@ snapshots: dependencies: '@types/node': 22.13.10 + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + optional: true + '@types/diff-match-patch@1.0.36': {} '@types/estree@1.0.6': {} @@ -9162,6 +9328,9 @@ snapshots: dependencies: '@types/node': 22.13.10 + '@types/ms@2.1.0': + optional: true + '@types/mysql@2.15.26': dependencies: '@types/node': 22.13.10 @@ -9436,6 +9605,10 @@ snapshots: react: 18.3.1 zod: 3.24.2 + ajv-draft-04@1.0.0(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -9697,10 +9870,14 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.2.7 + call-me-maybe@1.0.2: {} + callsites@3.1.0: {} camelcase@6.3.0: {} + camelcase@8.0.0: {} + caniuse-lite@1.0.30001678: {} chai@5.2.0: @@ -9883,6 +10060,8 @@ snapshots: decamelize@1.2.0: {} + decamelize@6.0.0: {} + decimal.js@10.5.0: {} decircular@0.1.1: {} @@ -10472,6 +10651,21 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execa@9.5.2: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.0 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + expect-type@1.2.0: {} express@4.21.2: @@ -10570,6 +10764,10 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -10740,6 +10938,11 @@ snapshots: get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -10902,6 +11105,8 @@ snapshots: human-signals@5.0.0: {} + human-signals@8.0.0: {} + humanize-ms@1.2.1: dependencies: ms: 2.1.3 @@ -11060,6 +11265,8 @@ snapshots: is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -11072,6 +11279,8 @@ snapshots: dependencies: which-typed-array: 1.1.15 + is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.0.2: @@ -11477,6 +11686,11 @@ snapshots: dependencies: path-key: 4.0.0 + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + nypm@0.6.0: dependencies: citty: 0.1.6 @@ -11667,6 +11881,8 @@ snapshots: index-to-position: 0.1.2 type-fest: 4.35.0 + parse-ms@4.0.0: {} + parseley@0.12.1: dependencies: leac: 0.6.0 @@ -11806,6 +12022,10 @@ snapshots: prettier@3.5.3: {} + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + process-warning@4.0.1: {} process@0.11.10: {} @@ -12317,6 +12537,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -12602,6 +12824,8 @@ snapshots: unicorn-magic@0.1.0: {} + unicorn-magic@0.3.0: {} + universal-github-app-jwt@2.2.0: {} universal-user-agent@7.0.2: {} @@ -12670,7 +12894,7 @@ snapshots: tsx: 4.19.3 yaml: 2.7.0 - vitest@3.0.9(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0): + vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0): dependencies: '@vitest/expect': 3.0.9 '@vitest/mocker': 3.0.9(vite@6.2.2(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) @@ -12693,6 +12917,7 @@ snapshots: vite-node: 3.0.9(@types/node@22.13.10)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: + '@types/debug': 4.1.12 '@types/node': 22.13.10 transitivePeerDependencies: - jiti @@ -12860,6 +13085,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.1.1: {} + zod-to-json-schema@3.24.3(zod@3.24.2): dependencies: zod: 3.24.2