diff --git a/legacy/packages/openapi-to-ts-client/fixtures/generated/notion-client.ts b/legacy/packages/openapi-to-ts-client/fixtures/generated/notion-client.ts new file mode 100644 index 00000000..39dca7cf --- /dev/null +++ b/legacy/packages/openapi-to-ts-client/fixtures/generated/notion-client.ts @@ -0,0 +1,1907 @@ +/* eslint-disable unicorn/no-unreadable-iife */ +/* eslint-disable unicorn/no-array-reduce */ + +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { + aiFunction, + AIFunctionsProvider, + assert, + getEnv, + pick, + sanitizeSearchParams +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod' + +export namespace notion { + export const apiBaseUrl = 'https://api.notion.so' + + // ----------------------------------------------------------------------------- + // Component schemas + // ----------------------------------------------------------------------------- + + export const UserObjectResponseSchema = z.object({ + object: z.literal('user'), + id: z.string(), + type: z.enum(['person', 'bot']), + name: z.string(), + avatar_url: z.string() + }) + export type UserObjectResponse = z.infer + + export const AnnotationRequestSchema = z.object({ + bold: z.boolean().optional(), + italic: z.boolean().optional(), + strikethrough: z.boolean().optional(), + underline: z.boolean().optional(), + code: z.boolean().optional(), + color: z + .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' + ]) + .optional() + }) + export type AnnotationRequest = z.infer + + export const DateRequestSchema = z.object({ + start: z.string(), + end: z.union([z.string(), z.null()]).optional(), + time_zone: z.union([z.string(), z.null()]).optional() + }) + export type DateRequest = z.infer + + export const PageObjectResponseSchema = z.object({ + object: z.literal('page'), + id: z.string(), + created_time: z.string(), + last_edited_time: z.string(), + archived: z.boolean(), + url: z.string() + }) + export type PageObjectResponse = z.infer + + export const PartialPageObjectResponseSchema = z.object({ + object: z.literal('page'), + id: z.string() + }) + export type PartialPageObjectResponse = z.infer< + typeof PartialPageObjectResponseSchema + > + + export const PropertyItemObjectResponseSchema = z.object({ + type: z.string(), + id: z.string() + }) + export type PropertyItemObjectResponse = z.infer< + typeof PropertyItemObjectResponseSchema + > + + export const PartialBlockObjectResponseSchema = z.object({ + object: z.literal('block'), + id: z.string() + }) + export type PartialBlockObjectResponse = z.infer< + typeof PartialBlockObjectResponseSchema + > + + export const BlockObjectResponseSchema = z.object({ + object: z.literal('block'), + id: z.string(), + type: z.string(), + created_time: z.string(), + last_edited_time: z.string(), + has_children: z.boolean(), + archived: z.boolean() + }) + export type BlockObjectResponse = z.infer + + export const TitlePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('title'), + title: z.record(z.any()) + }) + export type TitlePropertyResponse = z.infer< + typeof TitlePropertyResponseSchema + > + + export const RichTextPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('rich_text'), + rich_text: z.record(z.any()) + }) + export type RichTextPropertyResponse = z.infer< + typeof RichTextPropertyResponseSchema + > + + export const NumberPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('number'), + number: z.object({ format: z.string() }) + }) + export type NumberPropertyResponse = z.infer< + typeof NumberPropertyResponseSchema + > + + export const SelectOptionSchema = z.object({ + id: z.string(), + name: z.string(), + color: z.string() + }) + export type SelectOption = z.infer + + export const DatePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('date'), + date: z.record(z.any()) + }) + export type DatePropertyResponse = z.infer + + export const PeoplePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('people'), + people: z.record(z.any()) + }) + export type PeoplePropertyResponse = z.infer< + typeof PeoplePropertyResponseSchema + > + + export const FilePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('files'), + files: z.record(z.any()) + }) + export type FilePropertyResponse = z.infer + + export const CheckboxPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('checkbox'), + checkbox: z.record(z.any()) + }) + export type CheckboxPropertyResponse = z.infer< + typeof CheckboxPropertyResponseSchema + > + + export const URLPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('url'), + url: z.record(z.any()) + }) + export type URLPropertyResponse = z.infer + + export const EmailPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('email'), + email: z.record(z.any()) + }) + export type EmailPropertyResponse = z.infer< + typeof EmailPropertyResponseSchema + > + + export const PhoneNumberPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('phone_number'), + phone_number: z.record(z.any()) + }) + export type PhoneNumberPropertyResponse = z.infer< + typeof PhoneNumberPropertyResponseSchema + > + + export const FormulaPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('formula'), + formula: z.object({ expression: z.string() }) + }) + export type FormulaPropertyResponse = z.infer< + typeof FormulaPropertyResponseSchema + > + + export const RelationPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('relation'), + relation: z.object({ + database_id: z.string(), + synced_property_name: z.string(), + synced_property_id: z.string() + }) + }) + export type RelationPropertyResponse = z.infer< + typeof RelationPropertyResponseSchema + > + + export const RollupPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('rollup'), + rollup: z.object({ + relation_property_name: z.string(), + relation_property_id: z.string(), + rollup_property_name: z.string(), + rollup_property_id: z.string(), + function: z.string() + }) + }) + export type RollupPropertyResponse = z.infer< + typeof RollupPropertyResponseSchema + > + + export const CreatedTimePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('created_time'), + created_time: z.record(z.any()) + }) + export type CreatedTimePropertyResponse = z.infer< + typeof CreatedTimePropertyResponseSchema + > + + export const CreatedByPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('created_by'), + created_by: z.record(z.any()) + }) + export type CreatedByPropertyResponse = z.infer< + typeof CreatedByPropertyResponseSchema + > + + export const LastEditedTimePropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('last_edited_time'), + last_edited_time: z.record(z.any()) + }) + export type LastEditedTimePropertyResponse = z.infer< + typeof LastEditedTimePropertyResponseSchema + > + + export const LastEditedByPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('last_edited_by'), + last_edited_by: z.record(z.any()) + }) + export type LastEditedByPropertyResponse = z.infer< + typeof LastEditedByPropertyResponseSchema + > + + export const PartialUserObjectResponseSchema = z.object({ + object: z.literal('user'), + id: z.string() + }) + export type PartialUserObjectResponse = z.infer< + typeof PartialUserObjectResponseSchema + > + + export const AnnotationResponseSchema = z.object({ + bold: z.boolean(), + italic: z.boolean(), + strikethrough: z.boolean(), + underline: z.boolean(), + code: z.boolean(), + color: z.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' + ]) + }) + export type AnnotationResponse = z.infer + + export const DateResponseSchema = z.object({ + start: z.string(), + end: z.union([z.string(), z.null()]), + time_zone: z.union([z.string(), z.null()]) + }) + export type DateResponse = z.infer + + export const PropertyUpdateSchemaSchema = z.object({ + name: z.string().optional(), + type: z.string().optional() + }) + export type PropertyUpdateSchema = z.infer + + export const TextPropertyFilterSchema = z.object({ + equals: z.string().optional(), + does_not_equal: z.string().optional(), + contains: z.string().optional(), + does_not_contain: z.string().optional(), + starts_with: z.string().optional(), + ends_with: z.string().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type TextPropertyFilter = z.infer + + export const NumberPropertyFilterSchema = z.object({ + equals: z.number().optional(), + does_not_equal: z.number().optional(), + greater_than: z.number().optional(), + less_than: z.number().optional(), + greater_than_or_equal_to: z.number().optional(), + less_than_or_equal_to: z.number().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type NumberPropertyFilter = z.infer + + export const CheckboxPropertyFilterSchema = z.object({ + equals: z.boolean().optional(), + does_not_equal: z.boolean().optional() + }) + export type CheckboxPropertyFilter = z.infer< + typeof CheckboxPropertyFilterSchema + > + + export const SelectPropertyFilterSchema = z.object({ + equals: z.string().optional(), + does_not_equal: z.string().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type SelectPropertyFilter = z.infer + + export const MultiSelectPropertyFilterSchema = z.object({ + contains: z.string().optional(), + does_not_contain: z.string().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type MultiSelectPropertyFilter = z.infer< + typeof MultiSelectPropertyFilterSchema + > + + export const DatePropertyFilterSchema = z.object({ + equals: z.string().optional(), + before: z.string().optional(), + after: z.string().optional(), + on_or_before: z.string().optional(), + on_or_after: z.string().optional(), + past_week: z.any().optional(), + past_month: z.any().optional(), + past_year: z.any().optional(), + next_week: z.any().optional(), + next_month: z.any().optional(), + next_year: z.any().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type DatePropertyFilter = z.infer + + export const PeoplePropertyFilterSchema = z.object({ + contains: z.string().optional(), + does_not_contain: z.string().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type PeoplePropertyFilter = z.infer + + export const FilesPropertyFilterSchema = z.object({ + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type FilesPropertyFilter = z.infer + + export const RelationPropertyFilterSchema = z.object({ + contains: z.string().optional(), + does_not_contain: z.string().optional(), + is_empty: z.boolean().optional(), + is_not_empty: z.boolean().optional() + }) + export type RelationPropertyFilter = z.infer< + typeof RelationPropertyFilterSchema + > + + export const PropertySchemaSchema = z.object({ + type: z.string(), + name: z.union([z.string(), z.null()]).optional() + }) + export type PropertySchema = z.infer + + export const SearchParametersSchema = z.object({ + query: z.string().optional(), + sort: z + .object({ + direction: z.enum(['ascending', 'descending']), + timestamp: z.literal('last_edited_time') + }) + .optional(), + filter: z + .object({ + value: z.enum(['page', 'database']), + property: z.literal('object') + }) + .optional(), + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type SearchParameters = z.infer + + export const PartialCommentObjectResponseSchema = z.object({ + object: z.literal('comment'), + id: z.string() + }) + export type PartialCommentObjectResponse = z.infer< + typeof PartialCommentObjectResponseSchema + > + + export const OauthTokenParametersSchema = z.object({ + grant_type: z.string(), + code: z.string(), + redirect_uri: z.string().optional(), + external_account: z.object({ key: z.string(), name: z.string() }).optional() + }) + export type OauthTokenParameters = z.infer + + export const ListUsersResponseSchema = z.object({ + results: z.array(UserObjectResponseSchema), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type ListUsersResponse = z.infer + + export const PropertyItemListResponseSchema = z.object({ + results: z.array(PropertyItemObjectResponseSchema), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type PropertyItemListResponse = z.infer< + typeof PropertyItemListResponseSchema + > + + export const SelectPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('select'), + select: z.object({ options: z.array(SelectOptionSchema) }) + }) + export type SelectPropertyResponse = z.infer< + typeof SelectPropertyResponseSchema + > + + export const MultiSelectPropertyResponseSchema = z.object({ + id: z.string(), + type: z.literal('multi_select'), + multi_select: z.object({ options: z.array(SelectOptionSchema) }) + }) + export type MultiSelectPropertyResponse = z.infer< + typeof MultiSelectPropertyResponseSchema + > + + export const TextRichTextItemResponseSchema = z.object({ + type: z.literal('text'), + text: z.object({ + content: z.string(), + link: z.union([z.object({ url: z.string() }), z.null()]) + }), + annotations: AnnotationResponseSchema, + plain_text: z.string(), + href: z.union([z.string(), z.null()]) + }) + export type TextRichTextItemResponse = z.infer< + typeof TextRichTextItemResponseSchema + > + + export const EquationRichTextItemResponseSchema = z.object({ + type: z.literal('equation'), + equation: z.object({ expression: z.string() }), + annotations: AnnotationResponseSchema, + plain_text: z.string(), + href: z.union([z.string(), z.null()]) + }) + export type EquationRichTextItemResponse = z.infer< + typeof EquationRichTextItemResponseSchema + > + + export const ListBlockChildrenResponseSchema = z.object({ + object: z.literal('list'), + results: z.array( + z.union([PartialBlockObjectResponseSchema, BlockObjectResponseSchema]) + ), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type ListBlockChildrenResponse = z.infer< + typeof ListBlockChildrenResponseSchema + > + + export const AppendBlockChildrenResponseSchema = z.object({ + object: z.literal('list'), + results: z.array( + z.union([PartialBlockObjectResponseSchema, BlockObjectResponseSchema]) + ), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type AppendBlockChildrenResponse = z.infer< + typeof AppendBlockChildrenResponseSchema + > + + export const QueryDatabaseResponseSchema = z.object({ + object: z.literal('list'), + results: z.array( + z.union([PageObjectResponseSchema, PartialPageObjectResponseSchema]) + ), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type QueryDatabaseResponse = z.infer< + typeof QueryDatabaseResponseSchema + > + + export const OauthTokenResponseSchema = z.object({ + access_token: z.string(), + token_type: z.literal('bearer'), + bot_id: z.string(), + workspace_name: z.union([z.string(), z.null()]), + workspace_icon: z.union([z.string(), z.null()]), + workspace_id: z.string(), + owner: z.union([ + z.object({ + type: z.literal('user'), + user: z.union([ + UserObjectResponseSchema, + PartialUserObjectResponseSchema + ]) + }), + z.object({ type: z.literal('workspace'), workspace: z.literal(true) }) + ]), + duplicated_template_id: z.union([z.string(), z.null()]) + }) + export type OauthTokenResponse = z.infer + + export const RichTextItemRequestSchema = z.union([ + z.object({ + text: z.object({ + content: z.string(), + link: z.union([z.object({ url: z.string() }), z.null()]).optional() + }), + type: z.literal('text').optional(), + annotations: AnnotationRequestSchema.optional() + }), + z.object({ + mention: z.union([ + z.object({ + user: z.union([ + z.object({ id: z.string() }), + UserObjectResponseSchema + ]) + }), + z.object({ page: z.object({ id: z.string() }) }), + z.object({ database: z.object({ id: z.string() }) }), + z.object({ date: DateRequestSchema }) + ]), + type: z.literal('mention').optional(), + annotations: AnnotationRequestSchema.optional() + }), + z.object({ + equation: z.object({ expression: z.string() }), + type: z.literal('equation').optional(), + annotations: AnnotationRequestSchema.optional() + }) + ]) + export type RichTextItemRequest = z.infer + + export const CreatePageParametersSchema = z.object({ + parent: z + .record(z.any()) + .and( + z.union([ + z.object({ type: z.literal('page_id'), page_id: z.string() }), + z.object({ type: z.literal('database_id'), database_id: z.string() }) + ]) + ), + properties: z.record( + z.union([ + z.object({ title: z.array(RichTextItemRequestSchema) }), + z.object({ rich_text: z.array(RichTextItemRequestSchema) }), + z.object({ number: z.union([z.number(), z.null()]) }), + z.object({ + select: z.union([z.object({ name: z.string() }), z.null()]) + }) + ]) + ) + }) + export type CreatePageParameters = z.infer + + export const UpdatePageParametersSchema = z.object({ + properties: z + .record( + z.union([ + z.object({ title: z.array(RichTextItemRequestSchema) }), + z.object({ rich_text: z.array(RichTextItemRequestSchema) }), + z.object({ number: z.union([z.number(), z.null()]) }), + z.object({ + select: z.union([z.object({ name: z.string() }), z.null()]) + }) + ]) + ) + .optional(), + archived: z.boolean().optional() + }) + export type UpdatePageParameters = z.infer + + export const UpdateBlockParametersSchema = z.object({ + paragraph: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + heading_1: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + heading_2: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + heading_3: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + bulleted_list_item: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + numbered_list_item: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + quote: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + to_do: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + checked: z.boolean().optional(), + color: z.string().optional() + }) + .optional(), + toggle: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + color: z.string().optional() + }) + .optional(), + code: z + .object({ + rich_text: z.array(RichTextItemRequestSchema).optional(), + language: z.string().optional() + }) + .optional(), + embed: z.object({ url: z.string().optional() }).optional(), + image: z + .object({ external: z.object({ url: z.string().optional() }).optional() }) + .optional(), + video: z + .object({ external: z.object({ url: z.string().optional() }).optional() }) + .optional(), + file: z + .object({ external: z.object({ url: z.string().optional() }).optional() }) + .optional(), + pdf: z + .object({ external: z.object({ url: z.string().optional() }).optional() }) + .optional(), + bookmark: z.object({ url: z.string().optional() }).optional(), + equation: z.object({ expression: z.string().optional() }).optional(), + divider: z.record(z.any()).optional(), + table_of_contents: z.object({ color: z.string().optional() }).optional(), + breadcrumb: z.record(z.any()).optional(), + column_list: z.record(z.any()).optional(), + column: z.record(z.any()).optional(), + link_to_page: z + .object({ + type: z.enum(['page_id', 'database_id']).optional(), + page_id: z.string().optional(), + database_id: z.string().optional() + }) + .optional(), + table_row: z + .object({ cells: z.array(z.array(RichTextItemRequestSchema)).optional() }) + .optional(), + archived: z.boolean().optional() + }) + export type UpdateBlockParameters = z.infer< + typeof UpdateBlockParametersSchema + > + + export const BlockObjectRequestSchema = z.union([ + z.object({ + object: z.literal('block'), + type: z.literal('paragraph'), + paragraph: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('heading_1'), + heading_1: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('heading_2'), + heading_2: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('heading_3'), + heading_3: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('bulleted_list_item'), + bulleted_list_item: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('numbered_list_item'), + numbered_list_item: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('to_do'), + to_do: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + checked: z.boolean(), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('toggle'), + toggle: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + color: z.string().optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('code'), + code: z.object({ + rich_text: z.array(RichTextItemRequestSchema), + language: z.string(), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('child_page'), + child_page: z.object({ title: z.string() }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('child_database'), + child_database: z.object({ title: z.string() }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('embed'), + embed: z.object({ + url: z.string(), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('image'), + image: z.object({ + external: z.object({ url: z.string() }), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('video'), + video: z.object({ + external: z.object({ url: z.string() }), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('file'), + file: z.object({ + external: z.object({ url: z.string() }), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('pdf'), + pdf: z.object({ + external: z.object({ url: z.string() }), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('bookmark'), + bookmark: z.object({ + url: z.string(), + caption: z.array(RichTextItemRequestSchema).optional() + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('equation'), + equation: z.object({ expression: z.string() }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('divider'), + divider: z.record(z.any()) + }), + z.object({ + object: z.literal('block'), + type: z.literal('table_of_contents'), + table_of_contents: z.object({ color: z.string().optional() }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('column_list'), + column_list: z.record(z.any()) + }), + z.object({ + object: z.literal('block'), + type: z.literal('column'), + column: z.record(z.any()) + }), + z.object({ + object: z.literal('block'), + type: z.literal('link_to_page'), + link_to_page: z.union([ + z.object({ type: z.literal('page_id'), page_id: z.string() }), + z.object({ type: z.literal('database_id'), database_id: z.string() }) + ]) + }), + z.object({ + object: z.literal('block'), + type: z.literal('table'), + table: z.object({ + table_width: z.number().int(), + has_column_header: z.boolean().optional(), + has_row_header: z.boolean().optional(), + children: z.array( + // TODO: Support recursive types for `BlockObjectRequestSchema`. + z.any() + ) + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('table_row'), + table_row: z.object({ + cells: z.array(z.array(RichTextItemRequestSchema)) + }) + }), + z.object({ + object: z.literal('block'), + type: z.literal('synced_block'), + synced_block: z.object({ + synced_from: z + .union([ + z.object({ type: z.literal('block_id'), block_id: z.string() }), + z.null() + ]) + .optional(), + children: z + .array( + // TODO: Support recursive types for `BlockObjectRequestSchema`. + z.any() + ) + .optional() + }) + }) + ]) + export type BlockObjectRequest = z.infer + + export const MentionRichTextItemResponseSchema = z.object({ + type: z.literal('mention'), + mention: z.union([ + z.object({ + type: z.literal('user'), + user: z.union([ + PartialUserObjectResponseSchema, + UserObjectResponseSchema + ]) + }), + z.object({ type: z.literal('date'), date: DateResponseSchema }), + z.object({ + type: z.literal('link_preview'), + link_preview: z.object({ url: z.string() }) + }), + z.object({ type: z.literal('page'), page: z.object({ id: z.string() }) }), + z.object({ + type: z.literal('database'), + database: z.object({ id: z.string() }) + }) + ]), + annotations: AnnotationResponseSchema, + plain_text: z.string(), + href: z.union([z.string(), z.null()]) + }) + export type MentionRichTextItemResponse = z.infer< + typeof MentionRichTextItemResponseSchema + > + + export const CreateCommentParametersSchema = z.union([ + z.object({ + parent: z.object({ + page_id: z.string(), + type: z.literal('page_id').optional() + }), + rich_text: z.array(RichTextItemRequestSchema) + }), + z.object({ + discussion_id: z.string(), + rich_text: z.array(RichTextItemRequestSchema) + }) + ]) + export type CreateCommentParameters = z.infer< + typeof CreateCommentParametersSchema + > + + export const AppendBlockChildrenParametersSchema = z.object({ + children: z.array(BlockObjectRequestSchema) + }) + export type AppendBlockChildrenParameters = z.infer< + typeof AppendBlockChildrenParametersSchema + > + + export const UpdateDatabaseParametersSchema = z.object({ + title: z.array(RichTextItemRequestSchema).optional(), + description: z.array(RichTextItemRequestSchema).optional(), + icon: z + .union([ + z.object({ emoji: z.string(), type: z.literal('emoji') }), + z.object({ + external: z.object({ url: z.string() }), + type: z.literal('external') + }), + z.null() + ]) + .optional(), + cover: z + .union([ + z.object({ + external: z.object({ url: z.string() }), + type: z.literal('external') + }), + z.null() + ]) + .optional(), + properties: z.record(PropertyUpdateSchemaSchema).optional(), + is_inline: z.boolean().optional(), + archived: z.boolean().optional() + }) + export type UpdateDatabaseParameters = z.infer< + typeof UpdateDatabaseParametersSchema + > + + export const CreateDatabaseParametersSchema = z.object({ + parent: z.union([ + z.object({ type: z.literal('page_id'), page_id: z.string() }), + z.object({ type: z.literal('database_id'), database_id: z.string() }) + ]), + properties: z.record(PropertySchemaSchema), + icon: z + .union([ + z.object({ type: z.literal('emoji'), emoji: z.string() }), + z.object({ + type: z.literal('external'), + external: z.object({ url: z.string() }) + }), + z.null() + ]) + .optional(), + cover: z + .union([ + z.object({ + type: z.literal('external'), + external: z.object({ url: z.string() }) + }), + z.null() + ]) + .optional(), + title: z.array(RichTextItemRequestSchema), + description: z.array(RichTextItemRequestSchema).optional(), + is_inline: z.boolean().optional() + }) + export type CreateDatabaseParameters = z.infer< + typeof CreateDatabaseParametersSchema + > + + export const RichTextItemResponseSchema = z.union([ + TextRichTextItemResponseSchema, + MentionRichTextItemResponseSchema, + EquationRichTextItemResponseSchema + ]) + export type RichTextItemResponse = z.infer + + export const CommentObjectResponseSchema = z.object({ + object: z.literal('comment'), + id: z.string(), + parent: z.union([ + z.object({ type: z.literal('page_id'), page_id: z.string() }), + z.object({ type: z.literal('block_id'), block_id: z.string() }) + ]), + discussion_id: z.string(), + rich_text: z.array(RichTextItemResponseSchema), + created_by: PartialUserObjectResponseSchema, + created_time: z.string(), + last_edited_time: z.string() + }) + export type CommentObjectResponse = z.infer< + typeof CommentObjectResponseSchema + > + + export const PropertyFilterSchema = z.union([ + z.object({ property: z.string(), title: TextPropertyFilterSchema }), + z.object({ property: z.string(), rich_text: TextPropertyFilterSchema }), + z.object({ property: z.string(), number: NumberPropertyFilterSchema }), + z.object({ property: z.string(), checkbox: CheckboxPropertyFilterSchema }), + z.object({ property: z.string(), select: SelectPropertyFilterSchema }), + z.object({ + property: z.string(), + multi_select: MultiSelectPropertyFilterSchema + }), + z.object({ property: z.string(), date: DatePropertyFilterSchema }), + z.object({ property: z.string(), people: PeoplePropertyFilterSchema }), + z.object({ property: z.string(), files: FilesPropertyFilterSchema }), + z.object({ property: z.string(), url: TextPropertyFilterSchema }), + z.object({ property: z.string(), email: TextPropertyFilterSchema }), + z.object({ property: z.string(), phone_number: TextPropertyFilterSchema }), + z.object({ property: z.string(), relation: RelationPropertyFilterSchema }), + z.object({ property: z.string(), created_by: PeoplePropertyFilterSchema }), + z.object({ property: z.string(), created_time: DatePropertyFilterSchema }), + z.object({ + property: z.string(), + last_edited_by: PeoplePropertyFilterSchema + }), + z.object({ + property: z.string(), + last_edited_time: DatePropertyFilterSchema + }), + z.object({ + timestamp: z.enum(['created_time', 'last_edited_time']), + created_time: DatePropertyFilterSchema + }), + z.object({ + timestamp: z.enum(['created_time', 'last_edited_time']), + last_edited_time: DatePropertyFilterSchema + }) + ]) + export type PropertyFilter = z.infer + + export const ListCommentsResponseSchema = z.object({ + object: z.literal('list'), + results: z.array(CommentObjectResponseSchema), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type ListCommentsResponse = z.infer + + export const CompoundFilterSchema = z.object({ + and: z.array(PropertyFilterSchema).optional(), + or: z.array(PropertyFilterSchema).optional() + }) + export type CompoundFilter = z.infer + + export const QueryDatabaseParametersSchema = z.object({ + sorts: z + .array( + z.union([ + z.object({ + property: z.string(), + direction: z.enum(['ascending', 'descending']) + }), + z.object({ + timestamp: z.enum(['created_time', 'last_edited_time']), + direction: z.enum(['ascending', 'descending']) + }) + ]) + ) + .optional(), + filter: z.union([PropertyFilterSchema, CompoundFilterSchema]).optional(), + start_cursor: z.string().optional(), + page_size: z.number().int().optional(), + archived: z.boolean().optional() + }) + export type QueryDatabaseParameters = z.infer< + typeof QueryDatabaseParametersSchema + > + + export const DatabasePropertyConfigResponseSchema = z.union([ + TitlePropertyResponseSchema, + RichTextPropertyResponseSchema, + NumberPropertyResponseSchema, + SelectPropertyResponseSchema, + MultiSelectPropertyResponseSchema, + DatePropertyResponseSchema, + PeoplePropertyResponseSchema, + FilePropertyResponseSchema, + CheckboxPropertyResponseSchema, + URLPropertyResponseSchema, + EmailPropertyResponseSchema, + PhoneNumberPropertyResponseSchema, + FormulaPropertyResponseSchema, + RelationPropertyResponseSchema, + RollupPropertyResponseSchema, + CreatedTimePropertyResponseSchema, + CreatedByPropertyResponseSchema, + LastEditedTimePropertyResponseSchema, + LastEditedByPropertyResponseSchema + ]) + export type DatabasePropertyConfigResponse = z.infer< + typeof DatabasePropertyConfigResponseSchema + > + + export const PartialDatabaseObjectResponseSchema = z.object({ + object: z.literal('database'), + id: z.string(), + properties: z.record(DatabasePropertyConfigResponseSchema) + }) + export type PartialDatabaseObjectResponse = z.infer< + typeof PartialDatabaseObjectResponseSchema + > + + export const DatabaseObjectResponseSchema = z.object({ + object: z.literal('database'), + id: z.string(), + cover: z + .union([ + z.object({ + type: z.literal('external'), + external: z.object({ url: z.string() }) + }), + z.null() + ]) + .optional(), + icon: z + .union([ + z.object({ type: z.literal('emoji'), emoji: z.string() }), + z.object({ + type: z.literal('external'), + external: z.object({ url: z.string() }) + }), + z.null() + ]) + .optional(), + created_time: z.string(), + created_by: PartialUserObjectResponseSchema, + last_edited_time: z.string(), + last_edited_by: PartialUserObjectResponseSchema, + title: z.array(RichTextItemResponseSchema), + description: z.array(RichTextItemResponseSchema), + is_inline: z.boolean(), + properties: z.record(DatabasePropertyConfigResponseSchema), + parent: z.union([ + z.object({ type: z.literal('page_id'), page_id: z.string() }), + z.object({ type: z.literal('workspace'), workspace: z.literal(true) }) + ]), + url: z.string(), + archived: z.boolean() + }) + export type DatabaseObjectResponse = z.infer< + typeof DatabaseObjectResponseSchema + > + + export const ListDatabasesResponseSchema = z.object({ + object: z.literal('list'), + results: z.array( + z.union([ + PartialDatabaseObjectResponseSchema, + DatabaseObjectResponseSchema + ]) + ), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type ListDatabasesResponse = z.infer< + typeof ListDatabasesResponseSchema + > + + export const SearchResponseSchema = z.object({ + object: z.literal('list'), + results: z.array( + z.union([ + PageObjectResponseSchema, + PartialPageObjectResponseSchema, + PartialDatabaseObjectResponseSchema, + DatabaseObjectResponseSchema + ]) + ), + next_cursor: z.union([z.string(), z.null()]), + has_more: z.boolean() + }) + export type SearchResponse = z.infer + + // ----------------------------------------------------------------------------- + // Operation schemas + // ----------------------------------------------------------------------------- + + export const GetSelfParamsSchema = z.object({}) + export type GetSelfParams = z.infer + + export const GetSelfResponseSchema = UserObjectResponseSchema + export type GetSelfResponse = z.infer + + export const GetUserParamsSchema = z.object({ user_id: z.string() }) + export type GetUserParams = z.infer + + export const GetUserResponseSchema = UserObjectResponseSchema + export type GetUserResponse = z.infer + + export const ListUsersParamsSchema = z.object({ + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type ListUsersParams = z.infer + + export const CreatePageParamsSchema = CreatePageParametersSchema + export type CreatePageParams = z.infer + + export const CreatePageResponseSchema = z.union([ + PageObjectResponseSchema, + PartialPageObjectResponseSchema + ]) + export type CreatePageResponse = z.infer + + export const GetPageParamsSchema = z.object({ + page_id: z.string(), + filter_properties: z.array(z.string()).optional() + }) + export type GetPageParams = z.infer + + export const GetPageResponseSchema = z.union([ + PageObjectResponseSchema, + PartialPageObjectResponseSchema + ]) + export type GetPageResponse = z.infer + + export const UpdatePageParamsSchema = z + .object({ page_id: z.string() }) + .merge(UpdatePageParametersSchema) + export type UpdatePageParams = z.infer + + export const UpdatePageResponseSchema = z.union([ + PageObjectResponseSchema, + PartialPageObjectResponseSchema + ]) + export type UpdatePageResponse = z.infer + + export const GetPagePropertyParamsSchema = z.object({ + page_id: z.string(), + property_id: z.string(), + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type GetPagePropertyParams = z.infer< + typeof GetPagePropertyParamsSchema + > + + export const GetPagePropertyResponseSchema = z.union([ + PropertyItemObjectResponseSchema, + PropertyItemListResponseSchema + ]) + export type GetPagePropertyResponse = z.infer< + typeof GetPagePropertyResponseSchema + > + + export const GetBlockParamsSchema = z.object({ block_id: z.string() }) + export type GetBlockParams = z.infer + + export const GetBlockResponseSchema = z.union([ + PartialBlockObjectResponseSchema, + BlockObjectResponseSchema + ]) + export type GetBlockResponse = z.infer + + export const DeleteBlockParamsSchema = z.object({ block_id: z.string() }) + export type DeleteBlockParams = z.infer + + export const DeleteBlockResponseSchema = z.union([ + PartialBlockObjectResponseSchema, + BlockObjectResponseSchema + ]) + export type DeleteBlockResponse = z.infer + + export const UpdateBlockParamsSchema = z + .object({ block_id: z.string() }) + .merge(UpdateBlockParametersSchema) + export type UpdateBlockParams = z.infer + + export const UpdateBlockResponseSchema = z.union([ + PartialBlockObjectResponseSchema, + BlockObjectResponseSchema + ]) + export type UpdateBlockResponse = z.infer + + export const ListBlockChildrenParamsSchema = z.object({ + block_id: z.string(), + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type ListBlockChildrenParams = z.infer< + typeof ListBlockChildrenParamsSchema + > + + export const AppendBlockChildrenParamsSchema = z + .object({ block_id: z.string() }) + .merge(AppendBlockChildrenParametersSchema) + export type AppendBlockChildrenParams = z.infer< + typeof AppendBlockChildrenParamsSchema + > + + export const GetDatabaseParamsSchema = z.object({ database_id: z.string() }) + export type GetDatabaseParams = z.infer + + export const GetDatabaseResponseSchema = z.union([ + PartialDatabaseObjectResponseSchema, + DatabaseObjectResponseSchema + ]) + export type GetDatabaseResponse = z.infer + + export const UpdateDatabaseParamsSchema = z + .object({ database_id: z.string() }) + .merge(UpdateDatabaseParametersSchema) + export type UpdateDatabaseParams = z.infer + + export const UpdateDatabaseResponseSchema = z.union([ + PartialDatabaseObjectResponseSchema, + DatabaseObjectResponseSchema + ]) + export type UpdateDatabaseResponse = z.infer< + typeof UpdateDatabaseResponseSchema + > + + export const QueryDatabaseParamsSchema = z + .object({ + database_id: z.string(), + filter_properties: z.array(z.string()).optional() + }) + .merge(QueryDatabaseParametersSchema) + export type QueryDatabaseParams = z.infer + + export const ListDatabasesParamsSchema = z.object({ + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type ListDatabasesParams = z.infer + + export const CreateDatabaseParamsSchema = CreateDatabaseParametersSchema + export type CreateDatabaseParams = z.infer + + export const CreateDatabaseResponseSchema = z.union([ + PartialDatabaseObjectResponseSchema, + DatabaseObjectResponseSchema + ]) + export type CreateDatabaseResponse = z.infer< + typeof CreateDatabaseResponseSchema + > + + export const SearchParamsSchema = SearchParametersSchema + export type SearchParams = z.infer + + export const ListCommentsParamsSchema = z.object({ + block_id: z.string(), + start_cursor: z.string().optional(), + page_size: z.number().int().optional() + }) + export type ListCommentsParams = z.infer + + export const CreateCommentParamsSchema = CreateCommentParametersSchema + export type CreateCommentParams = z.infer + + export const CreateCommentResponseSchema = z.union([ + CommentObjectResponseSchema, + PartialCommentObjectResponseSchema + ]) + export type CreateCommentResponse = z.infer< + typeof CreateCommentResponseSchema + > + + export const OauthTokenParamsSchema = OauthTokenParametersSchema + export type OauthTokenParams = z.infer +} + +export class NotionClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + protected readonly apiKey: string + protected readonly apiBaseUrl: string + + constructor({ + apiKey = getEnv('NOTION_API_KEY'), + apiBaseUrl = notion.apiBaseUrl, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + assert( + apiKey, + 'NotionClient missing required "apiKey" (defaults to "NOTION_API_KEY")' + ) + super() + + this.apiKey = apiKey + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl, + headers: { + Authorization: `Bearer ${apiKey}` + } + }) + } + + /** + * Get current user. + */ + @aiFunction({ + name: 'get_self', + description: 'Get current user.', + inputSchema: notion.GetSelfParamsSchema + }) + async getSelf( + _params: notion.GetSelfParams + ): Promise { + return this.ky.get('/users/me').json() + } + + /** + * Get user. + */ + @aiFunction({ + name: 'get_user', + description: 'Get user.', + inputSchema: notion.GetUserParamsSchema + }) + async getUser(params: notion.GetUserParams): Promise { + return this.ky + .get(`/users/${params.user_id}`) + .json() + } + + /** + * List users. + */ + @aiFunction({ + name: 'list_users', + description: 'List users.', + inputSchema: notion.ListUsersParamsSchema + }) + async listUsers( + params: notion.ListUsersParams + ): Promise { + return this.ky + .get('/users', { + searchParams: sanitizeSearchParams(params) + }) + .json() + } + + /** + * Create page. + */ + @aiFunction({ + name: 'create_page', + description: 'Create page.', + inputSchema: notion.CreatePageParamsSchema + }) + async createPage( + params: notion.CreatePageParams + ): Promise { + return this.ky + .post('/pages', { + json: params + }) + .json() + } + + /** + * Get page. + */ + @aiFunction({ + name: 'get_page', + description: 'Get page.', + inputSchema: notion.GetPageParamsSchema + }) + async getPage(params: notion.GetPageParams): Promise { + return this.ky + .get(`/pages/${params.page_id}`, { + searchParams: sanitizeSearchParams(pick(params, 'filter_properties')) + }) + .json() + } + + /** + * Update page. + */ + @aiFunction({ + name: 'update_page', + description: 'Update page.', + inputSchema: notion.UpdatePageParamsSchema + }) + async updatePage( + params: notion.UpdatePageParams + ): Promise { + return this.ky + .patch(`/pages/${params.page_id}`, { + json: pick(params, 'properties', 'archived') + }) + .json() + } + + /** + * Get page property. + */ + @aiFunction({ + name: 'get_page_property', + description: 'Get page property.', + inputSchema: notion.GetPagePropertyParamsSchema + }) + async getPageProperty( + params: notion.GetPagePropertyParams + ): Promise { + return this.ky + .get(`/pages/${params.page_id}/properties/${params.property_id}`, { + searchParams: sanitizeSearchParams( + pick(params, 'start_cursor', 'page_size') + ) + }) + .json() + } + + /** + * Get block. + */ + @aiFunction({ + name: 'get_block', + description: 'Get block.', + inputSchema: notion.GetBlockParamsSchema + }) + async getBlock( + params: notion.GetBlockParams + ): Promise { + return this.ky + .get(`/blocks/${params.block_id}`) + .json() + } + + /** + * Delete block. + */ + @aiFunction({ + name: 'delete_block', + description: 'Delete block.', + inputSchema: notion.DeleteBlockParamsSchema + }) + async deleteBlock( + params: notion.DeleteBlockParams + ): Promise { + return this.ky + .delete(`/blocks/${params.block_id}`) + .json() + } + + /** + * Update block. + */ + @aiFunction({ + name: 'update_block', + description: 'Update block.', + inputSchema: notion.UpdateBlockParamsSchema + }) + async updateBlock( + params: notion.UpdateBlockParams + ): Promise { + return this.ky + .patch(`/blocks/${params.block_id}`, { + json: pick( + params, + 'paragraph', + 'heading_1', + 'heading_2', + 'heading_3', + 'bulleted_list_item', + 'numbered_list_item', + 'quote', + 'to_do', + 'toggle', + 'code', + 'embed', + 'image', + 'video', + 'file', + 'pdf', + 'bookmark', + 'equation', + 'divider', + 'table_of_contents', + 'breadcrumb', + 'column_list', + 'column', + 'link_to_page', + 'table_row', + 'archived' + ) + }) + .json() + } + + /** + * List block children. + */ + @aiFunction({ + name: 'list_block_children', + description: 'List block children.', + inputSchema: notion.ListBlockChildrenParamsSchema + }) + async listBlockChildren( + params: notion.ListBlockChildrenParams + ): Promise { + return this.ky + .get(`/blocks/${params.block_id}/children`, { + searchParams: sanitizeSearchParams( + pick(params, 'start_cursor', 'page_size') + ) + }) + .json() + } + + /** + * Append block children. + */ + @aiFunction({ + name: 'append_block_children', + description: 'Append block children.', + inputSchema: notion.AppendBlockChildrenParamsSchema + }) + async appendBlockChildren( + params: notion.AppendBlockChildrenParams + ): Promise { + return this.ky + .patch(`/blocks/${params.block_id}/children`, { + json: pick(params, 'children') + }) + .json() + } + + /** + * Get database. + */ + @aiFunction({ + name: 'get_database', + description: 'Get database.', + inputSchema: notion.GetDatabaseParamsSchema + }) + async getDatabase( + params: notion.GetDatabaseParams + ): Promise { + return this.ky + .get(`/databases/${params.database_id}`) + .json() + } + + /** + * Update database. + */ + @aiFunction({ + name: 'update_database', + description: 'Update database.', + inputSchema: notion.UpdateDatabaseParamsSchema + }) + async updateDatabase( + params: notion.UpdateDatabaseParams + ): Promise { + return this.ky + .patch(`/databases/${params.database_id}`, { + json: pick( + params, + 'title', + 'description', + 'icon', + 'cover', + 'properties', + 'is_inline', + 'archived' + ) + }) + .json() + } + + /** + * Query database. + */ + @aiFunction({ + name: 'query_database', + description: 'Query database.', + inputSchema: notion.QueryDatabaseParamsSchema + }) + async queryDatabase( + params: notion.QueryDatabaseParams + ): Promise { + return this.ky + .post(`/databases/${params.database_id}/query`, { + searchParams: sanitizeSearchParams(pick(params, 'filter_properties')), + json: pick( + params, + 'sorts', + 'filter', + 'start_cursor', + 'page_size', + 'archived' + ) + }) + .json() + } + + /** + * List databases. + */ + @aiFunction({ + name: 'list_databases', + description: 'List databases.', + inputSchema: notion.ListDatabasesParamsSchema + }) + async listDatabases( + params: notion.ListDatabasesParams + ): Promise { + return this.ky + .get('/databases', { + searchParams: sanitizeSearchParams(params) + }) + .json() + } + + /** + * Create database. + */ + @aiFunction({ + name: 'create_database', + description: 'Create database.', + inputSchema: notion.CreateDatabaseParamsSchema + }) + async createDatabase( + params: notion.CreateDatabaseParams + ): Promise { + return this.ky + .post('/databases', { + json: params + }) + .json() + } + + /** + * Search. + */ + @aiFunction({ + name: 'search', + description: 'Search.', + inputSchema: notion.SearchParamsSchema + }) + async search(params: notion.SearchParams): Promise { + return this.ky + .post('/search', { + json: params + }) + .json() + } + + /** + * List comments. + */ + @aiFunction({ + name: 'list_comments', + description: 'List comments.', + inputSchema: notion.ListCommentsParamsSchema + }) + async listComments( + params: notion.ListCommentsParams + ): Promise { + return this.ky + .get('/comments', { + searchParams: sanitizeSearchParams(params) + }) + .json() + } + + /** + * Create comment. + */ + @aiFunction({ + name: 'create_comment', + description: 'Create comment.', + // TODO: Improve handling of union params + inputSchema: notion.CreateCommentParamsSchema as any + }) + async createComment( + params: notion.CreateCommentParams + ): Promise { + return this.ky + .post('/comments', { + json: params + }) + .json() + } + + /** + * OAuth token. + */ + @aiFunction({ + name: 'oauth_token', + description: 'OAuth token.', + inputSchema: notion.OauthTokenParamsSchema + }) + async oauthToken( + params: notion.OauthTokenParams + ): Promise { + return this.ky + .post('/oauth/token', { + json: params + }) + .json() + } +} diff --git a/legacy/packages/openapi-to-ts-client/src/generate.ts b/legacy/packages/openapi-to-ts-client/src/generate.ts index 7de7e936..7c5fd830 100644 --- a/legacy/packages/openapi-to-ts-client/src/generate.ts +++ b/legacy/packages/openapi-to-ts-client/src/generate.ts @@ -19,6 +19,7 @@ import { getOperationParamsName, getOperationResponseName, jsonSchemaToZod, + naiveMergeJSONSchemas, prettify } from './utils' @@ -38,7 +39,9 @@ const httpMethods = [ ] as const async function main() { - const pathToOpenApiSpec = process.argv[2] + const pathToOpenApiSpec = + process.argv[2] ?? + path.join(dirname, '..', 'fixtures', 'openapi', 'notion.json') assert(pathToOpenApiSpec, 'Missing path to OpenAPI spec') const parser = new SwaggerParser() @@ -177,6 +180,7 @@ async function main() { operationIds.add(operationName) const operationNameSnakeCase = decamelize(operationName) + // if (path !== '/comments' || method !== 'post') continue // if (path !== '/crawl/status/{jobId}') continue // if (path !== '/pets' || method !== 'post') continue // console.log(method, path, operationName) @@ -191,6 +195,7 @@ async function main() { const operationResponseJSONSchemas: Record = {} const operationParamsSources: Record = {} + let operationParamsUnionSource: string | undefined // eslint-disable-next-line unicorn/consistent-function-scoping function addJSONSchemaParams(schema: IJsonSchema, source: string) { @@ -208,6 +213,16 @@ async function main() { ) operationParamsSources[key] = source } + } else if (derefed?.anyOf || derefed?.oneOf) { + const componentName = getComponentName(schema.$ref) + operationParamsSources[componentName] = source + + // TODO: handle this case + assert( + !operationParamsUnionSource, + `Duplicate union source ${source} for operation ${operationName}` + ) + operationParamsUnionSource = source } } else { assert(schema.type === 'object') @@ -233,6 +248,17 @@ async function main() { ...schema.required ] } + + if (schema?.anyOf || schema?.oneOf) { + operationParamsSources[schema.title || '__union__'] = source + + // TODO: handle this case + assert( + !operationParamsUnionSource, + `Duplicate union source ${source} for operation ${operationName}` + ) + operationParamsUnionSource = source + } } } @@ -308,9 +334,33 @@ async function main() { componentSchemas ) const operationResponseName = getOperationResponseName(operationName) + let derefedParams: any = dereference( + operationParamsJSONSchema, + parser.$refs + ) + for (const ref of derefedParams.$refs) { + const temp: any = dereference({ $ref: ref }, parser.$refs) + if (temp) { + derefedParams = naiveMergeJSONSchemas(derefedParams, temp) + } + } + // console.log(JSON.stringify(derefedParams, null, 2)) + const hasUnionParams = !!(derefedParams.anyOf || derefedParams.oneOf) + const hasParams = + Object.keys(derefedParams.properties ?? {}).length > 0 || hasUnionParams + + assert( + hasUnionParams === !!operationParamsUnionSource, + 'Unexpected union params' + ) + + // TODO: handle empty params case { // Merge all operations params into one schema declaration + // TODO: Don't generate this if it's only refs. We're currently handling + // this in a hacky way by removing the `z.object({}).merge(...)` down + // below. let operationsParamsSchema = jsonSchemaToZod( operationParamsJSONSchema, { name: `${operationParamsName}Schema`, type: operationParamsName } @@ -430,18 +480,18 @@ async function main() { ${description ? `/**\n * ${description}\n */` : ''} @aiFunction({ name: '${operationNameSnakeCase}', - ${description ? `description: '${description}',` : ''} - inputSchema: ${namespaceName}.${operationParamsName}Schema, + ${description ? `description: '${description}',` : ''}${hasUnionParams ? '\n// TODO: Improve handling of union params' : ''} + inputSchema: ${namespaceName}.${operationParamsName}Schema${hasUnionParams ? ' as any' : ''}, }) - async ${operationName}(params: ${namespaceName}.${operationParamsName}): Promise<${namespaceName}.${operationResponseName}> { + async ${operationName}(${!hasParams ? '_' : ''}params: ${namespaceName}.${operationParamsName}): Promise<${namespaceName}.${operationResponseName}> { return this.ky.${method}(${pathTemplate}${ - onlyHasPathParams + !hasParams || 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("', '")}'),`) : ''} + ${hasQueryParams ? (onlyHasOneParamsSource || hasUnionParams ? `searchParams: sanitizeSearchParams(params),` : `searchParams: sanitizeSearchParams(pick(params, '${queryParams.join("', '")}')),`) : ''} + ${hasBodyParams ? (onlyHasOneParamsSource || hasUnionParams ? `json: params,` : `json: pick(params, '${bodyParams.join("', '")}'),`) : ''} + ${hasFormDataParams ? (onlyHasOneParamsSource || hasUnionParams ? `form: params,` : `form: pick(params, '${formDataParams.join("', '")}'),`) : ''} + ${hasHeadersParams ? (onlyHasOneParamsSource || hasUnionParams ? `headers: params,` : `headers: pick(params, '${headersParams.join("', '")}'),`) : ''} }` }).json<${namespaceName}.${operationResponseName}>() } @@ -458,12 +508,11 @@ async function main() { > = {} for (const ref of componentsToProcess) { - const component = parser.$refs.get(ref) - assert(component) + const component = { $ref: ref } const resolved = new Set() const dereferenced = dereference(component, parser.$refs) - dereference(component, parser.$refs, resolved, 0, Number.POSITIVE_INFINITY) + dereferenceFull(component, parser.$refs, resolved) assert(dereferenced) for (const ref of resolved) { @@ -483,33 +532,25 @@ async function main() { const name = `${type}Schema` - const { dereferenced, refs } = componentToRefs[ref]! + const { dereferenced } = componentToRefs[ref]! if (processedComponents.has(ref)) { continue } - for (const r of refs) { - if (processedComponents.has(r)) { - continue - } - - processedComponents.add(r) - } - processedComponents.add(ref) + if (type === 'SearchResponse') { + console.log(type, dereferenced) + } + 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( + '\ncomponents', + Array.from(sortedComponents).map((ref) => getComponentName(ref)) + ) // console.log( // '\nmodels', @@ -525,6 +566,9 @@ async function main() { const aiClientMethodsString = aiClientMethods.join('\n\n') const header = ` +/* eslint-disable unicorn/no-unreadable-iife */ +/* eslint-disable unicorn/no-array-reduce */ + /** * This file was auto-generated from an OpenAPI spec. */ @@ -540,13 +584,20 @@ import { import defaultKy, { type KyInstance } from 'ky' import { z } from 'zod'`.trim() + const commentLine = `// ${'-'.repeat(77)}` const outputTypes = ( await prettify( [ header, `export namespace ${namespaceName} {`, apiBaseUrl ? `export const apiBaseUrl = '${apiBaseUrl}'` : undefined, + Object.values(componentSchemas).length + ? `${commentLine}\n// Component schemas\n${commentLine}` + : undefined, ...Object.values(componentSchemas), + Object.values(operationSchemas).length + ? `${commentLine}\n// Operation schemas\n${commentLine}` + : undefined, ...Object.values(operationSchemas), '}' ] @@ -554,7 +605,7 @@ import { z } from 'zod'`.trim() .join('\n\n') ) ) - .replaceAll(/z\.object\({}\)\.merge\(([^)]*)\)/g, '$1') + .replaceAll(/z\s*\.object\({}\)\s*\.merge\(([^)]*)\)/gm, '$1') .replaceAll(/\/\*\*(\S.*)\*\//g, '/** $1 */') const output = await prettify( @@ -593,7 +644,7 @@ export class ${clientName} extends AIFunctionsProvider { ${ hasGlobalApiKeyInHeader ? `headers: { - ${apiKeyHeaderNames.map((name) => `'${(resolvedSecuritySchemes[name] as any).name || 'Authorization'}': ${(resolvedSecuritySchemes[name] as any).schema?.toLowerCase() === 'bearer' ? '`Bearer ${apiKey}`' : 'apiKey'}`).join(',\n')} + ${apiKeyHeaderNames.map((name) => `'${(resolvedSecuritySchemes[name] as any).name || 'Authorization'}': ${(resolvedSecuritySchemes[name] as any).schema?.toLowerCase() === 'bearer' || resolvedSecuritySchemes[name]?.type?.toLowerCase() === 'oauth2' ? '`Bearer ${apiKey}`' : 'apiKey'}`).join(',\n')} },` : '' } diff --git a/legacy/packages/openapi-to-ts-client/src/utils.ts b/legacy/packages/openapi-to-ts-client/src/utils.ts index aba0364d..f348da68 100644 --- a/legacy/packages/openapi-to-ts-client/src/utils.ts +++ b/legacy/packages/openapi-to-ts-client/src/utils.ts @@ -1,9 +1,10 @@ import type SwaggerParser from '@apidevtools/swagger-parser' -import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types' +import type { IJsonSchema, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types' import { assert } from '@agentic/core' import { type JsonSchema, - jsonSchemaToZod as jsonSchemaToZodImpl + jsonSchemaToZod as jsonSchemaToZodImpl, + type ParserOverride } from 'json-schema-to-zod' import * as prettier from 'prettier' @@ -75,7 +76,8 @@ export function dereference( refs: SwaggerParser.$Refs, resolved?: Set, depth = 0, - maxDepth = 1 + maxDepth = 1, + visited = new Set() ): T { if (!obj) return obj @@ -85,23 +87,25 @@ export function dereference( if (Array.isArray(obj)) { return obj.map((item) => - dereference(item, refs, resolved, depth + 1, maxDepth) + dereference(item, refs, resolved, depth + 1, maxDepth, visited) ) as T } else if (typeof obj === 'object') { if ('$ref' in obj) { const ref = obj.$ref as string - const derefed = refs.get(ref) - if (!derefed) { + if (visited?.has(ref)) { return obj } + visited?.add(ref) + const derefed = refs.get(ref) + assert(derefed, `Invalid schema: $ref not found for ${ref}`) resolved?.add(ref) - derefed.title = ref.split('/').pop()! - return dereference(derefed, refs, resolved, depth + 1, maxDepth) + derefed.title ??= ref.split('/').pop()! + return dereference(derefed, refs, resolved, depth + 1, maxDepth, visited) } else { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key, - dereference(value, refs, resolved, depth + 1, maxDepth) + dereference(value, refs, resolved, depth + 1, maxDepth, visited) ]) ) as T } @@ -110,6 +114,47 @@ export function dereference( } } +function createParserOverride({ + type +}: { + type?: string +} = {}): ParserOverride { + const jsonSchemaToZodParserOverride: ParserOverride = (schema, _refs) => { + if ('$ref' in schema) { + const ref = schema.$ref as string + if (!ref) return + + const name = getComponentName(ref) + if (!name) return + + if (type === name) { + // TODO: Support recursive types. + return `\n// TODO: Support recursive types for \`${name}Schema\`.\nz.any()` + } + + return `${name}Schema` + } else if (schema.oneOf) { + const { oneOf, ...partialSchema } = schema + + // Replace oneOf with anyOf because `json-schema-to-zod` treats oneOf + // with a complicated `z.any().superRefine(...)` which we'd like messes + // up the resulting types. + const newSchema = { + ...partialSchema, + anyOf: oneOf + } + + const res = jsonSchemaToZodImpl(newSchema, { + parserOverride: jsonSchemaToZodParserOverride + }) + + return res + } + } + + return jsonSchemaToZodParserOverride +} + export function jsonSchemaToZod( schema: JsonSchema, { @@ -126,17 +171,7 @@ export function jsonSchemaToZod( 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` - } - } + parserOverride: createParserOverride({ type }) }) } @@ -251,3 +286,33 @@ export function getOperationResponseName( return tempName } + +export function naiveMergeJSONSchemas(...schemas: IJsonSchema[]): IJsonSchema { + const result: any = {} + + for (const ischema of schemas) { + const schema = ischema as any + const arrayKeys: string[] = [] + const objectKeys: string[] = [] + + for (const [key, value] of Object.entries(schema)) { + if (Array.isArray(value)) { + arrayKeys.push(key) + } else if (typeof value === 'object') { + objectKeys.push(key) + } else { + result[key] = value + } + } + + for (const key of arrayKeys) { + result[key] = [...(result[key] ?? []), ...(schema[key] ?? [])] + } + + for (const key of objectKeys) { + result[key] = { ...result[key], ...schema[key] } + } + } + + return result as IJsonSchema +}