Skip to content
File

Blob: types/src/worker/index.ts

typescript76 lines
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 
5import { StructureGroups } from '@workerd/jsg/rtti';
6import { Message } from 'capnp-es';
7import extraDefinitions from 'raw:../../defines';
8import comments from 'virtual:comments.json';
9import paramNames from 'virtual:param-names.json';
10import rtti from 'workerd:rtti';
11import { installParameterNames } from '../generator/parameter-names';
12import { printDefinitions } from '../index';
13 
14installParameterNames(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 */
20export 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;