Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[types] Generate @cloudflare/workers-types with a Worker #2405

Merged
merged 5 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions .github/workflows/npm.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Publish to NPM
on:
# Since we still need to manually upload binaries, use manual run
# Ideally this would trigger off `release`
# Since we still need to manually upload binaries, use manual run
# Ideally this would trigger off `release`
workflow_dispatch:
inputs:
patch:
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
fileName: workerd-${{ matrix.arch }}.gz
tarBall: false
zipBall: false
out-file-path: "release-downloads"
out-file-path: 'release-downloads'
token: ${{ secrets.GITHUB_TOKEN }}
# release-downloader does not support .gz files (unlike .tar.gz), decompress manually
# Using the -N flag the right file name should be restored
Expand Down Expand Up @@ -87,6 +87,28 @@ jobs:
with:
node-version: 18

- name: Cache
id: cache
uses: actions/cache@v4
# Use same cache and build configuration as release build, this allows us to keep download
# sizes small and generate types with optimization enabled, should be slightly faster.
with:
path: ~/bazel-disk-cache
key: bazel-disk-cache-release-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE') }}
- name: Setup Linux
run: |
export DEBIAN_FRONTEND=noninteractive
wget https://apt.llvm.org/llvm.sh
sed -i '/apt-get install/d' llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15
sudo apt-get install -y --no-install-recommends clang-15 lld-15 libunwind-15 libc++abi1-15 libc++1-15 libc++-15-dev
echo "build:linux --action_env=CC=/usr/lib/llvm-15/bin/clang --action_env=CXX=/usr/lib/llvm-15/bin/clang++" >> .bazelrc
echo "build:linux --host_action_env=CC=/usr/lib/llvm-15/bin/clang --host_action_env=CXX=/usr/lib/llvm-15/bin/clang++" >> .bazelrc
- name: Build type generating Worker
run: |
bazel build --disk_cache=~/bazel-disk-cache --strip=always --remote_cache=https://bazel:${{ secrets.BAZEL_CACHE_KEY }}@bazel-remote-cache.devprod.cloudflare.dev --config=release_linux //types:types_worker

- name: Modify package.json version
run: node npm/scripts/bump-version.mjs npm/workerd/package.json
env:
Expand Down
7 changes: 5 additions & 2 deletions npm/scripts/build-shim-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ function buildNeutralLib() {
'@cloudflare/workerd-darwin-64': process.env.WORKERD_VERSION,
'@cloudflare/workerd-linux-arm64': process.env.WORKERD_VERSION,
'@cloudflare/workerd-linux-64': process.env.WORKERD_VERSION,
'@cloudflare/workerd-windows-64': process.env.WORKERD_VERSION
'@cloudflare/workerd-windows-64': process.env.WORKERD_VERSION,
};
fs.writeFileSync(pjPath, JSON.stringify(package_json, null, 2) + '\n');

const capnpPath = path.join('src', 'workerd', 'server', 'workerd.capnp');

fs.copyFileSync(capnpPath, path.join('npm', 'workerd', 'workerd.capnp'))
fs.copyFileSync(capnpPath, path.join('npm', 'workerd', 'workerd.capnp'));

const typeWorkerPath = path.join('bazel-bin', 'types', 'dist', 'index.mjs');

fs.copyFileSync(typeWorkerPath, path.join('npm', 'workerd', 'worker.mjs'));
}

buildNeutralLib();
53 changes: 49 additions & 4 deletions src/workerd/api/rtti.c++
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
#include <workerd/api/urlpattern.h>
#include <workerd/api/node/node.h>
#include <workerd/jsg/modules.capnp.h>
#include <workerd/api/hyperdrive.h>
#include <workerd/api/eventsource.h>
#include <workerd/api/unsafe.h>
#include <workerd/api/memory-cache.h>
#include <workerd/api/worker-rpc.h>

#include <cloudflare/cloudflare.capnp.h>

Expand All @@ -53,6 +58,10 @@
F("form-data", EW_FORMDATA_ISOLATE_TYPES) \
F("html-rewriter", EW_HTML_REWRITER_ISOLATE_TYPES) \
F("http", EW_HTTP_ISOLATE_TYPES) \
F("hyperdrive", EW_HYPERDRIVE_ISOLATE_TYPES) \
F("unsafe", EW_UNSAFE_ISOLATE_TYPES) \
F("memory-cache", EW_MEMORY_CACHE_ISOLATE_TYPES) \
F("pyodide", EW_PYODIDE_ISOLATE_TYPES) \
F("kv", EW_KV_ISOLATE_TYPES) \
F("queue", EW_QUEUE_ISOLATE_TYPES) \
F("r2-admin", EW_R2_PUBLIC_BETA_ADMIN_ISOLATE_TYPES) \
Expand All @@ -69,7 +78,8 @@
F("sockets", EW_SOCKETS_ISOLATE_TYPES) \
F("node", EW_NODE_ISOLATE_TYPES) \
F("rtti", EW_RTTI_ISOLATE_TYPES) \
F("webgpu", EW_WEBGPU_ISOLATE_TYPES)
F("webgpu", EW_WEBGPU_ISOLATE_TYPES) \
F("eventsource", EW_EVENTSOURCE_ISOLATE_TYPES)

namespace workerd::api {

Expand Down Expand Up @@ -148,14 +158,44 @@ CompatibilityFlags::Reader compileFlags(capnp::MessageBuilder &message, kj::Stri
return kj::mv(reader);
}

CompatibilityFlags::Reader compileAllFlags(capnp::MessageBuilder &message) {
auto output = message.initRoot<CompatibilityFlags>();
auto schema = capnp::Schema::from<CompatibilityFlags>();
auto dynamicOutput = capnp::toDynamic(output);
for (auto field: schema.getFields()) {
bool isNode = false;

kj::StringPtr enableFlagName;

for (auto annotation: field.getProto().getAnnotations()) {
if (annotation.getId() == COMPAT_ENABLE_FLAG_ANNOTATION_ID) {
enableFlagName = annotation.getValue().getText();
// Exclude nodejs_compat, since the type generation scripts don't support node:* imports
// TODO: Figure out typing for node compat
isNode = enableFlagName == "nodejs_compat" ||
penalosa marked this conversation as resolved.
Show resolved Hide resolved
enableFlagName == "nodejs_compat_v2";
}
}

dynamicOutput.set(field, !isNode);
}
auto reader = output.asReader();
return kj::mv(reader);
}

struct TypesEncoder {
public:
TypesEncoder(): compatFlags(kj::heapArray<kj::String>(0)) {}
TypesEncoder(kj::String compatDate, kj::Array<kj::String> compatFlags): compatDate(kj::mv(compatDate)), compatFlags(kj::mv(compatFlags)) {}

kj::Array<byte> encode() {
capnp::MallocMessageBuilder flagsMessage;
CompatibilityFlags::Reader flags = compileFlags(flagsMessage, compatDate, false, compatFlags);

CompatibilityFlags::Reader flags;
KJ_IF_SOME(date, compatDate) {
flags = compileFlags(flagsMessage, date, true, compatFlags);
} else {
flags = compileAllFlags(flagsMessage);
}
capnp::MallocMessageBuilder message;
auto root = message.initRoot<jsg::rtti::StructureGroups>();

Expand Down Expand Up @@ -221,7 +261,7 @@ private:
KJ_ASSERT(structureIndex == structuresSize);
}

kj::String compatDate;
kj::Maybe<kj::String> compatDate;
kj::Array<kj::String> compatFlags;

unsigned int groupsIndex = 0;
Expand All @@ -235,4 +275,9 @@ kj::Array<byte> RTTIModule::exportTypes(kj::String compatDate, kj::Array<kj::Str
return encoder.encode();
}

kj::Array<byte> RTTIModule::exportExperimentalTypes() {
TypesEncoder encoder;
return encoder.encode();
}

} // namespace workerd::api
2 changes: 2 additions & 0 deletions src/workerd/api/rtti.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ class RTTIModule final: public jsg::Object {
RTTIModule(jsg::Lock&, const jsg::Url&) {}

kj::Array<byte> exportTypes(kj::String compatDate, kj::Array<kj::String> compatFlags);
kj::Array<byte> exportExperimentalTypes();

JSG_RESOURCE_TYPE(RTTIModule) {
JSG_METHOD(exportTypes);
JSG_METHOD(exportExperimentalTypes);
}
};

Expand Down
121 changes: 7 additions & 114 deletions src/workerd/tools/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,133 +5,26 @@ load("//:build/run_binary_target.bzl", "run_binary_target")
load("//:build/wd_cc_binary.bzl", "wd_cc_binary")
load("//:build/wd_cc_library.bzl", "wd_cc_library")

# ========================================================================================
# C++ deps for API extraction
#
# Both `api_encoder` and `param_extractor` need access to the API surface
# of `workerd`, so this target allows them to both have the same deps

wd_cc_library(
name = "api_encoder_lib",
deps = [
"//src/workerd/api:html-rewriter",
"//src/workerd/io",
"//src/workerd/jsg",
"//src/workerd/jsg:rtti",
"@capnp-cpp//src/capnp",
],
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)

api_encoder_src = "api-encoder.c++"

# ========================================================================================
# API Encoder
#
# Encodes runtime API type information into a capnp schema

wd_cc_binary(
name = "api_encoder_bin",
srcs = [api_encoder_src],
deps = [":api_encoder_lib"],
# Use dynamic linkage where possible to reduce binary size – unlike the workerd binary, we
# shouldn't need to distribute the api encoder.
linkstatic = 0,
# The dependent targets are not Windows-compatible, no need to compile this.
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)

# All compatibility dates that changed public facing types.
# Remember to update `npm/workers-types/README.md` when adding new dates here.
compat_dates = [
# Oldest compatibility date, with no flags enabled
("2021-01-01", "oldest"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#formdata-parsing-supports-file
("2021-11-03", "2021-11-03"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#settersgetters-on-api-object-prototypes
("2022-01-31", "2022-01-31"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#global-navigator
("2022-03-21", "2022-03-21"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#r2-bucket-list-respects-the-include-option
("2022-08-04", "2022-08-04"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#new-url-parser-implementation
("2022-10-31", "2022-10-31"),
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#streams-constructors
# https://developers.cloudflare.com/workers/platform/compatibility-dates/#compliant-transformstream-constructor
("2022-11-30", "2022-11-30"),
# https://github.com/cloudflare/workerd/blob/fcb6f33d10c71975cb2ce68dbf1924a1eeadbd8a/src/workerd/io/compatibility-date.capnp#L275-L280 (http_headers_getsetcookie)
("2023-03-01", "2023-03-01"),
# https://github.com/cloudflare/workerd/blob/fcb6f33d10c71975cb2ce68dbf1924a1eeadbd8a/src/workerd/io/compatibility-date.capnp#L307-L312 (urlsearchparams_delete_has_value_arg)
("2023-07-01", "2023-07-01"),
# Latest compatibility date (note these types should be the same as the previous entry)
(None, "experimental"),
]

filegroup(
name = "api_encoder",
srcs = [
"//src/workerd/tools:api_encoder_" + label
for (date, label) in compat_dates
],
tags = ["no-arm64"],
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
)

[
run_binary_target(
name = "api_encoder_" + label,
outs = [label + ".api.capnp.bin"],
args = [
"--output",
"$(location " + label + ".api.capnp.bin)",
] + ([
"--compatibility-date",
date,
] if date else []),
# Cross-compiling is not supported as this runs in target cfg.
tags = ["no-arm64"],
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
tool = "api_encoder_bin",
visibility = ["//visibility:public"],
)
for (date, label) in compat_dates
]

# ========================================================================================
# Parameter Name Extractor
#
# Extracts the parameter names of functions, methods, etc. of the runtime API,
# since they're not encoded in the type information generated by `api_encoder`

cc_library(
name = "compile_api_headers_only",
defines = ["API_ENCODER_HDRS_ONLY=1"],
)
# since they're not encoded in the type information generated by the RTTI dump

cc_ast_dump(
name = "dump_api_ast",
src = api_encoder_src,
src = "param-names-ast.c++",
out = "api.ast.json.gz",
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
deps = [
":api_encoder_lib",
":compile_api_headers_only",
"//src/workerd/api:html-rewriter",
"//src/workerd/io",
"//src/workerd/jsg",
"//src/workerd/jsg:rtti",
"@capnp-cpp//src/capnp",
],
)

Expand Down
Loading
Loading