/** * List every supported Canva Connect operation slug with method, path, tag, and preview/deprecated flags. * Use this to discover slugs before calling `./api`; mutations still require `confirm: true` there. * * @returns `{ count, operations }` keyed by lowercase OpenAPI slug. * * @example * import operationsCatalog from 'kody:@kentcdodds/canva/operations' * const catalog = await operationsCatalog() * // => { count: 40, operations: { listfolderitems: { method: 'GET', path: '/v1/folders/{folderId}/items', tag: 'folder' }, ... } } */ export type CanvaOperationDefinition = { method: 'GET' | 'POST' | 'PATCH' | 'DELETE' path: string tag: string contentType?: 'application/json' | 'application/octet-stream' deprecated?: boolean preview?: boolean } export const canvaOperations = { createassetuploadjob: { method: 'POST', path: '/v1/asset-uploads', tag: 'asset', contentType: 'application/octet-stream', }, createcomment: { method: 'POST', path: '/v1/comments', tag: 'comment', contentType: 'application/json', deprecated: true, }, createdesign: { method: 'POST', path: '/v1/designs', tag: 'design', contentType: 'application/json', }, createdesignautofilljob: { method: 'POST', path: '/v1/autofills', tag: 'autofill', contentType: 'application/json', }, createdesignexportjob: { method: 'POST', path: '/v1/exports', tag: 'export', contentType: 'application/json', }, createdesignimportjob: { method: 'POST', path: '/v1/imports', tag: 'design-import', contentType: 'application/octet-stream', }, createdesignmergejob: { method: 'POST', path: '/v1/merges', tag: 'merge', contentType: 'application/json', preview: true, }, createdesignresizejob: { method: 'POST', path: '/v1/resizes', tag: 'resize', contentType: 'application/json', }, createfolder: { method: 'POST', path: '/v1/folders', tag: 'folder', contentType: 'application/json', }, createprintpartnerdesign: { method: 'POST', path: '/v1/print-partner/designs', tag: 'design', contentType: 'application/json', preview: true, }, createprintpartnerdesignexportjob: { method: 'POST', path: '/v1/print-partner/exports', tag: 'export', contentType: 'application/json', preview: true, }, createreply: { method: 'POST', path: '/v1/designs/{designId}/comments/{threadId}/replies', tag: 'comment', contentType: 'application/json', preview: true, }, createthread: { method: 'POST', path: '/v1/designs/{designId}/comments', tag: 'comment', contentType: 'application/json', preview: true, }, createurlassetuploadjob: { method: 'POST', path: '/v1/url-asset-uploads', tag: 'asset', contentType: 'application/json', preview: true, }, createurlimportjob: { method: 'POST', path: '/v1/url-imports', tag: 'design-import', contentType: 'application/json', }, deleteasset: { method: 'DELETE', path: '/v1/assets/{assetId}', tag: 'asset' }, deletefolder: { method: 'DELETE', path: '/v1/folders/{folderId}', tag: 'folder' }, getasset: { method: 'GET', path: '/v1/assets/{assetId}', tag: 'asset' }, getassetuploadjob: { method: 'GET', path: '/v1/asset-uploads/{jobId}', tag: 'asset' }, getbrandtemplate: { method: 'GET', path: '/v1/brand-templates/{brandTemplateId}', tag: 'brand-template', }, getbrandtemplatedataset: { method: 'GET', path: '/v1/brand-templates/{brandTemplateId}/dataset', tag: 'brand-template', }, getdesign: { method: 'GET', path: '/v1/designs/{designId}', tag: 'design' }, getdesignautofilljob: { method: 'GET', path: '/v1/autofills/{jobId}', tag: 'autofill' }, getdesigndataset: { method: 'GET', path: '/v1/designs/{designId}/dataset', tag: 'design', preview: true, }, getdesignexportformats: { method: 'GET', path: '/v1/designs/{designId}/export-formats', tag: 'design', }, getdesignexportjob: { method: 'GET', path: '/v1/exports/{exportId}', tag: 'export' }, getdesignimportjob: { method: 'GET', path: '/v1/imports/{jobId}', tag: 'design-import' }, getdesignmergejob: { method: 'GET', path: '/v1/merges/{jobId}', tag: 'merge', preview: true, }, getdesignpages: { method: 'GET', path: '/v1/designs/{designId}/pages', tag: 'design', preview: true, }, getdesignresizejob: { method: 'GET', path: '/v1/resizes/{jobId}', tag: 'resize' }, getfolder: { method: 'GET', path: '/v1/folders/{folderId}', tag: 'folder' }, getoidcjwks: { method: 'GET', path: '/v1/oidc/jwks', tag: 'oidc' }, getreply: { method: 'GET', path: '/v1/designs/{designId}/comments/{threadId}/replies/{replyId}', tag: 'comment', preview: true, }, getthread: { method: 'GET', path: '/v1/designs/{designId}/comments/{threadId}', tag: 'comment', preview: true, }, geturlassetuploadjob: { method: 'GET', path: '/v1/url-asset-uploads/{jobId}', tag: 'asset', preview: true, }, geturlimportjob: { method: 'GET', path: '/v1/url-imports/{jobId}', tag: 'design-import' }, getusercapabilities: { method: 'GET', path: '/v1/users/me/capabilities', tag: 'user' }, getuserprofile: { method: 'GET', path: '/v1/users/me/profile', tag: 'user' }, listbrandtemplates: { method: 'GET', path: '/v1/brand-templates', tag: 'brand-template' }, listdesigns: { method: 'GET', path: '/v1/designs', tag: 'design' }, listfolderitems: { method: 'GET', path: '/v1/folders/{folderId}/items', tag: 'folder', }, listreplies: { method: 'GET', path: '/v1/designs/{designId}/comments/{threadId}/replies', tag: 'comment', preview: true, }, movefolderitem: { method: 'POST', path: '/v1/folders/move', tag: 'folder', contentType: 'application/json', }, publishbrandtemplate: { method: 'POST', path: '/v1/brand-templates', tag: 'brand-template', contentType: 'application/json', preview: true, }, updateasset: { method: 'PATCH', path: '/v1/assets/{assetId}', tag: 'asset', contentType: 'application/json', }, updatefolder: { method: 'PATCH', path: '/v1/folders/{folderId}', tag: 'folder', contentType: 'application/json', }, userinfo: { method: 'GET', path: '/v1/oidc/userinfo', tag: 'oidc' }, usersme: { method: 'GET', path: '/v1/users/me', tag: 'user' }, } as const satisfies Record export type CanvaOperation = keyof typeof canvaOperations export const canvaOperationNames = Object.freeze( Object.keys(canvaOperations) as Array, ) export function isCanvaOperation(value: unknown): value is CanvaOperation { return typeof value === 'string' && Object.hasOwn(canvaOperations, value) } export function isMutation(operation: CanvaOperation): boolean { return canvaOperations[operation].method !== 'GET' } /** * List every supported Canva Connect operation slug with method, path, tag, and preview/deprecated flags. * Use this to discover slugs before calling `./api`; mutations still require `confirm: true` there. * * @returns `{ count, operations }` keyed by lowercase OpenAPI slug. * * @example * import operationsCatalog from 'kody:@kentcdodds/canva/operations' * const catalog = await operationsCatalog() * // => { count: 40, operations: { listfolderitems: { method: 'GET', path: '/v1/folders/{folderId}/items', tag: 'folder' }, ... } } */ export default async function operationsCatalog() { return { count: canvaOperationNames.length, operations: canvaOperations, } }