File
Blob: types/src/worker/index.ts
| 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | import { StructureGroups } from '@workerd/jsg/rtti'; |
| 6 | import { Message } from 'capnp-es'; |
| 7 | import extraDefinitions from 'raw:../../defines'; |
| 8 | import comments from 'virtual:comments.json'; |
| 9 | import paramNames from 'virtual:param-names.json'; |
| 10 | import rtti from 'workerd:rtti'; |
| 11 | import { installParameterNames } from '../generator/parameter-names'; |
| 12 | import { printDefinitions } from '../index'; |
| 13 | |
| 14 | installParameterNames(paramNames); |
| 15 | /** |
| 16 | * Worker for generating TypeScript types based on an arbitrary compatibility date |
| 17 | * and optional compatibility flags. Accepts paths of the form `/<compat-date>(+<compat_flag>)*` |
| 18 | * (e.g. `/2024-01-01+nodejs_compat+no_global_navigator`) |
| 19 | */ |
| 20 | export default { |
| 21 | async fetch(request): Promise<Response> { |
| 22 | try { |
| 23 | // Ensure pathname matches expected pattern |
| 24 | let { pathname } = new URL(request.url); |
| 25 | if ( |
| 26 | !/^\/\d{4}-\d{2}-\d{2}\+?/.test(pathname) && |
| 27 | !pathname.startsWith('/experimental') |
| 28 | ) { |
| 29 | return new Response('Not Found', { status: 404 }); |
| 30 | } |
| 31 | |
| 32 | // Extract response format |
| 33 | let format: 'ambient' | 'importable' | 'bundle' = 'ambient'; |
| 34 | if (pathname.endsWith('.d.ts')) { |
| 35 | pathname = pathname.slice(0, -'.d.ts'.length); |
| 36 | } else if (pathname.endsWith('.ts')) { |
| 37 | pathname = pathname.slice(0, -'.ts'.length); |
| 38 | format = 'importable'; |
| 39 | } else if (pathname.endsWith('.bundle')) { |
| 40 | pathname = pathname.slice(0, -'.bundle'.length); |
| 41 | format = 'bundle'; |
| 42 | } |
| 43 | |
| 44 | // Export RTTI for specified compatibility settings |
| 45 | const [compatDate, ...compatFlags] = pathname.substring(1).split('+'); |
| 46 | let rttiCapnpBuffer: ArrayBuffer; |
| 47 | if (compatDate === 'experimental') { |
| 48 | rttiCapnpBuffer = rtti.exportExperimentalTypes(); |
| 49 | } else { |
| 50 | rttiCapnpBuffer = rtti.exportTypes(compatDate, compatFlags); |
| 51 | } |
| 52 | |
| 53 | const message = new Message(rttiCapnpBuffer, /* packed */ false); |
| 54 | const root = message.getRoot(StructureGroups); |
| 55 | |
| 56 | // Generate and return types in correct format |
| 57 | const { ambient, importable } = printDefinitions( |
| 58 | root, |
| 59 | comments, |
| 60 | extraDefinitions |
| 61 | ); |
| 62 | |
| 63 | if (format === 'ambient') return new Response(ambient); |
| 64 | |
| 65 | if (format === 'importable') return new Response(importable); |
| 66 | |
| 67 | const bundle = new FormData(); |
| 68 | bundle.set('index.d.ts', ambient); |
| 69 | bundle.set('index.ts', importable); |
| 70 | return new Response(bundle); |
| 71 | } catch (e) { |
| 72 | return new Response(e.stack, { status: 500 }); |
| 73 | } |
| 74 | }, |
| 75 | } satisfies ExportedHandler; |