File
Blob: src/workerd/api/global-scope.h
| 1 | // Copyright (c) 2017-2022 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 | #pragma once |
| 6 | |
| 7 | #include "basics.h" |
| 8 | #include "filesystem.h" |
| 9 | #include "http.h" |
| 10 | #include "messagechannel.h" |
| 11 | #include "performance.h" |
| 12 | |
| 13 | #include <workerd/api/hibernation-event-params.h> |
| 14 | #ifdef WORKERD_FUZZILLI |
| 15 | #include "unsafe.h" |
| 16 | |
| 17 | #include <workerd/api/fuzzilli.h> |
| 18 | #endif |
| 19 | |
| 20 | #include <workerd/io/features.h> |
| 21 | #include <workerd/io/io-timers.h> |
| 22 | #include <workerd/jsg/jsg.h> |
| 23 | |
| 24 | namespace workerd::jsg { |
| 25 | class DOMException; |
| 26 | } // namespace workerd::jsg |
| 27 | |
| 28 | namespace workerd::api { |
| 29 | |
| 30 | class Tracing; |
| 31 | class TailEvent; |
| 32 | class Cache; |
| 33 | class CacheStorage; |
| 34 | class JsRpcProperty; |
| 35 | class Crypto; |
| 36 | class CryptoKey; |
| 37 | class ErrorEvent; |
| 38 | class EventSource; |
| 39 | class FixedLengthStream; |
| 40 | class SubtleCrypto; |
| 41 | class TextDecoder; |
| 42 | class TextEncoder; |
| 43 | class HTMLRewriter; |
| 44 | class IdentityTransformStream; |
| 45 | class Response; |
| 46 | class TraceItem; |
| 47 | class TransformStream; |
| 48 | class ScheduledController; |
| 49 | class ScheduledEvent; |
| 50 | class ReadableStream; |
| 51 | class ReadableStreamDefaultReader; |
| 52 | class ReadableStreamBYOBReader; |
| 53 | class ReadableStreamBYOBRequest; |
| 54 | class WritableStream; |
| 55 | class WritableStreamDefaultWriter; |
| 56 | class ReadableStreamBYOBRequest; |
| 57 | class ReadableStreamDefaultController; |
| 58 | class ReadableByteStreamController; |
| 59 | class WritableStreamDefaultController; |
| 60 | class CompressionStream; |
| 61 | class DecompressionStream; |
| 62 | class TextEncoderStream; |
| 63 | class TextDecoderStream; |
| 64 | class Blob; |
| 65 | class File; |
| 66 | class FormData; |
| 67 | |
| 68 | class URLPattern; |
| 69 | namespace urlpattern { |
| 70 | class URLPattern; |
| 71 | } // namespace urlpattern |
| 72 | |
| 73 | class URL; |
| 74 | class URLSearchParams; |
| 75 | namespace url { |
| 76 | class URL; |
| 77 | class URLSearchParams; |
| 78 | } // namespace url |
| 79 | |
| 80 | // We need access to DOMException within this namespace so JSG_NESTED_TYPE can name it correctly. |
| 81 | using DOMException = jsg::DOMException; |
| 82 | |
| 83 | // A subset of the standard Navigator API. |
| 84 | class Navigator: public jsg::Object { |
| 85 | public: |
| 86 | kj::StringPtr getUserAgent() { |
| 87 | return "Cloudflare-Workers"_kj; |
| 88 | } |
| 89 | |
| 90 | bool sendBeacon(jsg::Lock& js, kj::String url, jsg::Optional<Body::Initializer> body); |
| 91 | |
| 92 | kj::uint getHardwareConcurrency() { |
| 93 | // Workers does not expose hardware concurrency to users. |
| 94 | // From the user code perspective there's only one core. |
| 95 | return 1; |
| 96 | } |
| 97 | |
| 98 | kj::StringPtr getPlatform() { |
| 99 | return ""_kj; |
| 100 | } |
| 101 | |
| 102 | kj::StringPtr getLanguage() { |
| 103 | // Some packages depend on navigator.language being set to a specific value. |
| 104 | return "en"_kj; |
| 105 | } |
| 106 | |
| 107 | kj::Array<kj::StringPtr> getLanguages() { |
| 108 | auto builder = kj::heapArrayBuilder<kj::StringPtr>(1); |
| 109 | builder.add("en"_kjc); |
| 110 | return builder.finish(); |
| 111 | } |
| 112 | |
| 113 | jsg::Ref<StorageManager> getStorage(jsg::Lock& js); |
| 114 | |
| 115 | JSG_RESOURCE_TYPE(Navigator, CompatibilityFlags::Reader reader) { |
| 116 | JSG_METHOD(sendBeacon); |
| 117 | JSG_READONLY_INSTANCE_PROPERTY(userAgent, getUserAgent); |
| 118 | JSG_READONLY_INSTANCE_PROPERTY(hardwareConcurrency, getHardwareConcurrency); |
| 119 | JSG_READONLY_INSTANCE_PROPERTY(platform, getPlatform); |
| 120 | |
| 121 | if (reader.getEnableNavigatorLanguage()) { |
| 122 | JSG_READONLY_INSTANCE_PROPERTY(language, getLanguage); |
| 123 | JSG_READONLY_INSTANCE_PROPERTY(languages, getLanguages); |
| 124 | } |
| 125 | |
| 126 | if (reader.getWebFileSystem()) { |
| 127 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(storage, getStorage); |
| 128 | } |
| 129 | |
| 130 | JSG_TS_OVERRIDE({ |
| 131 | sendBeacon(url: string, body?: BodyInit): boolean; |
| 132 | }); |
| 133 | } |
| 134 | }; |
| 135 | |
| 136 | // Exposed as a global to provide access to certain Cloudflare-specific |
| 137 | // configuration details. This is not a standard API and great care should |
| 138 | // be taken when deciding to expose new properties or methods here. |
| 139 | class Cloudflare: public jsg::Object { |
| 140 | public: |
| 141 | // Return an object containing the state of all compatibility flags known to the runtime. |
| 142 | jsg::JsObject getCompatibilityFlags(jsg::Lock& js); |
| 143 | |
| 144 | JSG_RESOURCE_TYPE(Cloudflare) { |
| 145 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(compatibilityFlags, getCompatibilityFlags); |
| 146 | |
| 147 | JSG_TS_OVERRIDE({ readonly compatibilityFlags: Record<string, boolean>; |
| 148 | }); |
| 149 | } |
| 150 | }; |
| 151 | |
| 152 | class WorkerGlobalScope: public EventTarget, public jsg::ContextGlobal { |
| 153 | public: |
| 154 | jsg::Unimplemented importScripts(kj::String s) { |
| 155 | return {}; |
| 156 | }; |
| 157 | |
| 158 | JSG_RESOURCE_TYPE(WorkerGlobalScope, CompatibilityFlags::Reader flags) { |
| 159 | JSG_INHERIT(EventTarget); |
| 160 | |
| 161 | // *** WARNING ***: *Every* new export here must be treated as a potentially |
| 162 | // breaking change. It doesn't matter if it's a new method, a new property, |
| 163 | // or a new nested type. Adding anything to the global scope risks breaking |
| 164 | // existing user code that is feature-sniffing or monkeypatching the global. |
| 165 | // *Always* add new exports behind a new compatibility flag! And when in |
| 166 | // doubt, don't add the new export on globalThis at all. The only things |
| 167 | // that should be exported on globalThis should be standardized web APIs or |
| 168 | // Node.js compat mode globals. |
| 169 | |
| 170 | JSG_NESTED_TYPE(EventTarget); |
| 171 | |
| 172 | if (!flags.getNoImportScripts()) { |
| 173 | JSG_METHOD(importScripts); |
| 174 | } |
| 175 | |
| 176 | JSG_TS_DEFINE(type WorkerGlobalScopeEventMap = { |
| 177 | fetch: FetchEvent; |
| 178 | scheduled: ScheduledEvent; |
| 179 | queue: QueueEvent; |
| 180 | unhandledrejection: PromiseRejectionEvent; |
| 181 | rejectionhandled: PromiseRejectionEvent; |
| 182 | }); |
| 183 | JSG_TS_OVERRIDE(extends EventTarget<WorkerGlobalScopeEventMap>); |
| 184 | } |
| 185 | |
| 186 | // Because EventTarget has a constructor(), we have to explicitly delete |
| 187 | // the constructor() here or we'll end up with compilation errors |
| 188 | // (EventTarget's constructor confuses the hasConstructorMethod in resource.h) |
| 189 | static jsg::Ref<WorkerGlobalScope> constructor() = delete; |
| 190 | }; |
| 191 | |
| 192 | // Controller type for test handler. |
| 193 | // |
| 194 | // At present, this has no methods. It is defined for consistency with other handlers and on the |
| 195 | // assumption that we'll probably want to put something here someday. |
| 196 | class TestController: public jsg::Object { |
| 197 | public: |
| 198 | JSG_RESOURCE_TYPE(TestController) {} |
| 199 | }; |
| 200 | |
| 201 | // Structured types for the cache purge API (ctx.cache.purge()). |
| 202 | // These match the coreless-purge-ingest WorkersCachePurgeEntrypoint types. |
| 203 | // NOTE: TypeScript stubs for CachePurgeError, CachePurgeResult, CachePurgeOptions, and |
| 204 | // CacheContext are manually maintained in src/cloudflare/internal/workers.d.ts. If you change |
| 205 | // these types, update that file to match. |
| 206 | struct CachePurgeError { |
| 207 | int code; |
| 208 | kj::String message; |
| 209 | JSG_STRUCT(code, message); |
| 210 | }; |
| 211 | |
| 212 | struct CachePurgeResult { |
| 213 | bool success; |
| 214 | kj::Array<CachePurgeError> errors; |
| 215 | JSG_STRUCT(success, errors); |
| 216 | }; |
| 217 | |
| 218 | struct CachePurgeOptions { |
| 219 | jsg::Optional<kj::Array<kj::String>> tags; |
| 220 | jsg::Optional<kj::Array<kj::String>> pathPrefixes; |
| 221 | jsg::Optional<bool> purgeEverything; |
| 222 | JSG_STRUCT(tags, pathPrefixes, purgeEverything); |
| 223 | }; |
| 224 | |
| 225 | // Base class for the ctx.cache object on cache enabled Workers. |
| 226 | // Subclass when embedding to provide an implementation. |
| 227 | class CacheContext: public jsg::Object { |
| 228 | public: |
| 229 | // Purge cached content. |
| 230 | // |
| 231 | // The default implementation throws without an overriding IoContext. |
| 232 | virtual jsg::Promise<CachePurgeResult> purge(jsg::Lock& js, |
| 233 | CachePurgeOptions options, |
| 234 | const jsg::TypeHandler<CachePurgeOptions>& optionsHandler, |
| 235 | const jsg::TypeHandler<CachePurgeResult>& resultHandler, |
| 236 | const jsg::TypeHandler<jsg::Ref<JsRpcProperty>>& rpcPropHandler); |
| 237 | |
| 238 | JSG_RESOURCE_TYPE(CacheContext) { |
| 239 | JSG_METHOD(purge); |
| 240 | } |
| 241 | }; |
| 242 | |
| 243 | class ExecutionContext: public jsg::Object { |
| 244 | public: |
| 245 | ExecutionContext(jsg::Lock& js, jsg::JsValue exports) |
| 246 | : ExecutionContext(js, kj::mv(exports), /*props=*/js.obj(), /*versionInfo=*/kj::none) {} |
| 247 | |
| 248 | ExecutionContext(jsg::Lock& js, |
| 249 | jsg::JsValue exports, |
| 250 | jsg::JsValue props, |
| 251 | kj::Maybe<Worker::VersionInfo> versionInfo) |
| 252 | : exports(js, exports), |
| 253 | props(js, props) { |
| 254 | auto featureFlags = FeatureFlags::get(js); |
| 255 | if (featureFlags.getEnableVersionApi()) { |
| 256 | version = kj::mv(versionInfo).map([&js, &featureFlags](auto v) { |
| 257 | return jsg::JsRef{js, |
| 258 | v.toJs(js, |
| 259 | featureFlags.getEnableCtxVersionMetadata() ? PopulateVersionInfoMetadata::YES |
| 260 | : PopulateVersionInfoMetadata::NO)}; |
| 261 | }); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | void waitUntil(kj::Promise<void> promise); |
| 266 | void passThroughOnException(); |
| 267 | |
| 268 | // Cancels the current execution context with the given exception, causing all execution to stop |
| 269 | // and throwing an error at the client. |
| 270 | void abort(jsg::Lock& js, jsg::Optional<jsg::Value> reason); |
| 271 | |
| 272 | jsg::JsValue getExports(jsg::Lock& js) { |
| 273 | return exports.getHandle(js); |
| 274 | } |
| 275 | |
| 276 | jsg::JsValue getProps(jsg::Lock& js) { |
| 277 | return props.getHandle(js); |
| 278 | } |
| 279 | |
| 280 | // Returns a CacheContext for cache-enabled workers, or empty jsg::Optional otherwise. |
| 281 | // However, by default this always returns undefined unless the embedding application overrides |
| 282 | // the IoContext. |
| 283 | jsg::Optional<jsg::Ref<CacheContext>> getCache(jsg::Lock& js); |
| 284 | |
| 285 | jsg::JsValue getVersion(jsg::Lock& js) { |
| 286 | // TODO(soon): We should be able to assert for `version != kj::none` in the constructor when the |
| 287 | // `enable_version_api` compat flag is enabled, but currently dynamic workers and "reusable |
| 288 | // `ctx` object instantiation" do not pass information to populate `ctx.version` with, |
| 289 | // `ctx.version` is currently optional so we can return undefined for now. |
| 290 | KJ_IF_SOME(someVersion, version) { |
| 291 | return someVersion.getHandle(js); |
| 292 | } |
| 293 | return js.undefined(); |
| 294 | } |
| 295 | |
| 296 | jsg::Optional<jsg::Ref<Tracing>> getTracing(jsg::Lock& js); |
| 297 | |
| 298 | JSG_RESOURCE_TYPE(ExecutionContext, CompatibilityFlags::Reader flags) { |
| 299 | JSG_METHOD(waitUntil); |
| 300 | JSG_METHOD(passThroughOnException); |
| 301 | if (flags.getEnableCtxExports()) { |
| 302 | JSG_LAZY_INSTANCE_PROPERTY(exports, getExports); |
| 303 | } |
| 304 | JSG_LAZY_INSTANCE_PROPERTY(props, getProps); |
| 305 | JSG_LAZY_INSTANCE_PROPERTY(cache, getCache); |
| 306 | if (flags.getEnableVersionApi()) { |
| 307 | JSG_LAZY_INSTANCE_PROPERTY(version, getVersion); |
| 308 | } |
| 309 | |
| 310 | // ctx.tracing - user tracing API. The *type* is always visible (so the generated |
| 311 | // `Tracing` / `Span` types exist in every compat-date snapshot, not only the |
| 312 | // experimental one). The *value* is `undefined` outside the `workerdExperimental` |
| 313 | // compat flag - the gate lives in `getTracing()` in global-scope.c++. |
| 314 | // TODO: Remove this comment once the feature is stable. |
| 315 | JSG_LAZY_INSTANCE_PROPERTY(tracing, getTracing); |
| 316 | |
| 317 | if (flags.getWorkerdExperimental()) { |
| 318 | // TODO(soon): Before making this generally available we need to: |
| 319 | // * Consider whether to use TerminateExecution() instead of throwing. |
| 320 | // * Make sure it's really not possible for more code to run in the context after abort(). |
| 321 | // Currently, abort() triggers in a partially async way so there's an opportunity for some |
| 322 | // other event in the event queue to squeeze in. |
| 323 | // * Try to ensure that the provided error is actually the one that propagates out of event |
| 324 | // handlers. Currently this is not consistently true. |
| 325 | // * Make sure all event handlers actually honor onAbort(). |
| 326 | // * Enable the Durable Object version at the same time -- and make sure they're suitably |
| 327 | // consistent with each other. |
| 328 | JSG_METHOD(abort); |
| 329 | } |
| 330 | |
| 331 | // TODO(soon): This is getting unwieldy. |
| 332 | if (flags.getEnableCtxExports()) { |
| 333 | if (flags.getEnableVersionApi()) { |
| 334 | JSG_TS_OVERRIDE(<Props = unknown> { |
| 335 | readonly props: Props; |
| 336 | readonly exports: Cloudflare.Exports; |
| 337 | readonly version?: { |
| 338 | readonly metadata?: { readonly id: string; }; |
| 339 | readonly cohort?: string; |
| 340 | readonly key?: string; |
| 341 | readonly override?: string; |
| 342 | }; |
| 343 | }); |
| 344 | } else { |
| 345 | JSG_TS_OVERRIDE(<Props = unknown> { |
| 346 | readonly props: Props; |
| 347 | readonly exports: Cloudflare.Exports; |
| 348 | }); |
| 349 | } |
| 350 | } else { |
| 351 | if (flags.getEnableVersionApi()) { |
| 352 | JSG_TS_OVERRIDE(<Props = unknown> { |
| 353 | readonly props: Props; |
| 354 | readonly version?: { |
| 355 | readonly metadata?: { readonly id: string; }; |
| 356 | readonly cohort?: string; |
| 357 | readonly key?: string; |
| 358 | readonly override?: string; |
| 359 | }; |
| 360 | }); |
| 361 | } else { |
| 362 | JSG_TS_OVERRIDE(<Props = unknown> { |
| 363 | readonly props: Props; |
| 364 | }); |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | void visitForMemoryInfo(jsg::MemoryTracker& tracker) const { |
| 370 | tracker.trackField("props", props); |
| 371 | tracker.trackField("version", version); |
| 372 | } |
| 373 | |
| 374 | private: |
| 375 | jsg::JsRef<jsg::JsValue> exports; |
| 376 | jsg::JsRef<jsg::JsValue> props; |
| 377 | kj::Maybe<jsg::JsRef<jsg::JsValue>> version; |
| 378 | |
| 379 | void visitForGc(jsg::GcVisitor& visitor) { |
| 380 | visitor.visit(props); |
| 381 | visitor.visit(version); |
| 382 | } |
| 383 | }; |
| 384 | |
| 385 | // AlarmEventInfo is a jsg::Object used to pass alarm invocation info to an alarm handler. |
| 386 | class AlarmInvocationInfo: public jsg::Object { |
| 387 | public: |
| 388 | AlarmInvocationInfo(kj::Date scheduledTime, uint32_t retry) |
| 389 | : scheduledTime(static_cast<double>((scheduledTime - kj::UNIX_EPOCH) / kj::MILLISECONDS)), |
| 390 | retryCount(retry) {} |
| 391 | |
| 392 | bool getIsRetry() { |
| 393 | return retryCount > 0; |
| 394 | } |
| 395 | uint32_t getRetryCount() { |
| 396 | return retryCount; |
| 397 | } |
| 398 | double getScheduledTime() { |
| 399 | return scheduledTime; |
| 400 | } |
| 401 | |
| 402 | JSG_RESOURCE_TYPE(AlarmInvocationInfo) { |
| 403 | JSG_READONLY_INSTANCE_PROPERTY(isRetry, getIsRetry); |
| 404 | JSG_READONLY_INSTANCE_PROPERTY(retryCount, getRetryCount); |
| 405 | JSG_READONLY_INSTANCE_PROPERTY(scheduledTime, getScheduledTime); |
| 406 | } |
| 407 | |
| 408 | private: |
| 409 | double scheduledTime; |
| 410 | uint32_t retryCount = 0; |
| 411 | }; |
| 412 | |
| 413 | // Type signature for handlers exported from the root module. |
| 414 | // |
| 415 | // We define each handler method as a LenientOptional rather than as a plain Optional in order to |
| 416 | // treat incorrect types as if the field is undefined. Without this, Durable Object class |
| 417 | // constructors that set a field with one of these names would cause confusing type errors. |
| 418 | struct ExportedHandler { |
| 419 | using FetchHandler = jsg::Promise<jsg::Ref<api::Response>>(jsg::Ref<api::Request> request, |
| 420 | jsg::Value env, |
| 421 | jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 422 | jsg::LenientOptional<jsg::Function<FetchHandler>> fetch; |
| 423 | |
| 424 | using ConnectHandler = jsg::Promise<void>( |
| 425 | jsg::Ref<Socket> socket, jsg::Value env, jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 426 | jsg::LenientOptional<jsg::Function<ConnectHandler>> connect; |
| 427 | |
| 428 | using TailHandler = kj::Promise<void>(kj::Array<jsg::Ref<TraceItem>> events, |
| 429 | jsg::Value env, |
| 430 | jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 431 | jsg::LenientOptional<jsg::Function<TailHandler>> tail; |
| 432 | jsg::LenientOptional<jsg::Function<TailHandler>> trace; |
| 433 | |
| 434 | using TailStreamHandler = kj::Promise<void>( |
| 435 | jsg::JsObject obj, jsg::Value env, jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 436 | jsg::LenientOptional<jsg::Function<TailStreamHandler>> tailStream; |
| 437 | |
| 438 | using ScheduledHandler = kj::Promise<void>(jsg::Ref<ScheduledController> controller, |
| 439 | jsg::Value env, |
| 440 | jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 441 | jsg::LenientOptional<jsg::Function<ScheduledHandler>> scheduled; |
| 442 | |
| 443 | using AlarmHandler = kj::Promise<void>(jsg::Ref<AlarmInvocationInfo> alarmInfo); |
| 444 | // Alarms are only exported on DOs, which receive env bindings from the constructor |
| 445 | jsg::LenientOptional<jsg::Function<AlarmHandler>> alarm; |
| 446 | |
| 447 | using TestHandler = jsg::Promise<void>(jsg::Ref<TestController> controller, |
| 448 | jsg::Value env, |
| 449 | jsg::Optional<jsg::Ref<ExecutionContext>> ctx); |
| 450 | jsg::LenientOptional<jsg::Function<TestHandler>> test; |
| 451 | |
| 452 | using HibernatableWebSocketMessageHandler = kj::Promise<void>( |
| 453 | jsg::Ref<WebSocket>, kj::OneOf<kj::String, kj::Array<byte>> message); |
| 454 | jsg::LenientOptional<jsg::Function<HibernatableWebSocketMessageHandler>> webSocketMessage; |
| 455 | |
| 456 | using HibernatableWebSocketCloseHandler = kj::Promise<void>( |
| 457 | jsg::Ref<WebSocket>, int code, kj::String reason, bool wasClean); |
| 458 | jsg::LenientOptional<jsg::Function<HibernatableWebSocketCloseHandler>> webSocketClose; |
| 459 | |
| 460 | using HibernatableWebSocketErrorHandler = kj::Promise<void>(jsg::Ref<WebSocket>, jsg::Value); |
| 461 | jsg::LenientOptional<jsg::Function<HibernatableWebSocketErrorHandler>> webSocketError; |
| 462 | |
| 463 | // Self-ref potentially allows extracting other custom handlers from the object. |
| 464 | jsg::SelfRef self; |
| 465 | |
| 466 | JSG_STRUCT(fetch, |
| 467 | connect, |
| 468 | tail, |
| 469 | trace, |
| 470 | tailStream, |
| 471 | scheduled, |
| 472 | alarm, |
| 473 | test, |
| 474 | webSocketMessage, |
| 475 | webSocketClose, |
| 476 | webSocketError, |
| 477 | self); |
| 478 | |
| 479 | JSG_STRUCT_TS_ROOT(); |
| 480 | // ExportedHandler isn't included in the global scope, but we still want to |
| 481 | // include it in type definitions. |
| 482 | |
| 483 | JSG_STRUCT_TS_DEFINE( |
| 484 | type ExportedHandlerFetchHandler<Env = unknown, CfHostMetadata = unknown, Props = unknown> = (request: Request<CfHostMetadata, IncomingRequestCfProperties<CfHostMetadata>>, env: Env, ctx: ExecutionContext<Props>) => Response | Promise<Response>; |
| 485 | type ExportedHandlerConnectHandler<Env = unknown, Props = unknown> = (socket: Socket, env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 486 | type ExportedHandlerTailHandler<Env = unknown, Props = unknown> = (events: TraceItem[], env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 487 | type ExportedHandlerTraceHandler<Env = unknown, Props = unknown> = (traces: TraceItem[], env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 488 | type ExportedHandlerTailStreamHandler<Env = unknown, Props = unknown> = (event : TailStream.TailEvent<TailStream.Onset>, env: Env, ctx: ExecutionContext<Props>) => TailStream.TailEventHandlerType | Promise<TailStream.TailEventHandlerType>; |
| 489 | type ExportedHandlerScheduledHandler<Env = unknown, Props = unknown> = (controller: ScheduledController, env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 490 | type ExportedHandlerQueueHandler<Env = unknown, Message = unknown, Props = unknown> = (batch: MessageBatch<Message>, env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 491 | type ExportedHandlerTestHandler<Env = unknown, Props = unknown> = (controller: TestController, env: Env, ctx: ExecutionContext<Props>) => void | Promise<void>; |
| 492 | ); |
| 493 | JSG_STRUCT_TS_OVERRIDE(<Env = unknown, QueueHandlerMessage = unknown, CfHostMetadata = unknown, Props = unknown> { |
| 494 | email?: EmailExportedHandler<Env, Props>; |
| 495 | fetch?: ExportedHandlerFetchHandler<Env, CfHostMetadata, Props>; |
| 496 | connect?: ExportedHandlerConnectHandler<Env, Props>; |
| 497 | tail?: ExportedHandlerTailHandler<Env, Props>; |
| 498 | trace?: ExportedHandlerTraceHandler<Env, Props>; |
| 499 | tailStream?: ExportedHandlerTailStreamHandler<Env, Props>; |
| 500 | scheduled?: ExportedHandlerScheduledHandler<Env, Props>; |
| 501 | alarm: never; |
| 502 | webSocketMessage: never; |
| 503 | webSocketClose: never; |
| 504 | webSocketError: never; |
| 505 | queue?: ExportedHandlerQueueHandler<Env, QueueHandlerMessage, Props>; |
| 506 | test?: ExportedHandlerTestHandler<Env, Props>; |
| 507 | }); |
| 508 | // Make `env` parameter generic |
| 509 | |
| 510 | // Values to pass for `env` and `ctx` when calling handlers. Note these have to be the last members |
| 511 | // so that they don't interfere with `JSG_STRUCT`'s machinations. |
| 512 | |
| 513 | // env and ctx values that need to be passed to the handler function. If the ExportedHandler |
| 514 | // represents a class instance (e.g. Durable Object instance), then `env` is is the JS value |
| 515 | // `undefined` and `ctx` is `kj::none`. |
| 516 | // TODO(cleanup): Why isn't `env` a `jsg::Optional` too? Or maybe the pair should be wrapped in |
| 517 | // a struct that is `Maybe`? |
| 518 | jsg::Value env = nullptr; |
| 519 | jsg::Optional<jsg::Ref<ExecutionContext>> ctx = kj::none; |
| 520 | // TODO(cleanup): These are shoved here as a bit of a hack. At present, this is convenient and |
| 521 | // works for all use cases. If we have bindings or things on ctx that vary on a per-request basis, |
| 522 | // this won't work as well, I guess, but we can cross that bridge when we come to it. |
| 523 | |
| 524 | // If true, this is a Durable Object class that failed to extend `DurableObject`. We will not |
| 525 | // permit RPC to this class. |
| 526 | bool missingSuperclass = false; |
| 527 | |
| 528 | jsg::Optional<jsg::Ref<ExecutionContext>> getCtx() { |
| 529 | return ctx.map([&](jsg::Ref<ExecutionContext>& p) { return p.addRef(); }); |
| 530 | } |
| 531 | |
| 532 | ExportedHandler clone(jsg::Lock& js); |
| 533 | }; |
| 534 | |
| 535 | // An approximation of Node.js setImmediate `Immediate` object. |
| 536 | // This is used only when the `nodejs_compat_v2` compatibility flag is enabled. |
| 537 | class Immediate final: public jsg::Object { |
| 538 | public: |
| 539 | Immediate(IoContext& context, TimeoutId timeoutId); |
| 540 | |
| 541 | // In Node.js, the "ref" mechanism refers to whether or not an i/o object |
| 542 | // will keep the libuv event loop alive (and therefore keep the process alive). |
| 543 | // We do not implement a similar mechanism in workerd. These are here only to |
| 544 | // satisfy the API contract for the `Immediate` object but are never expected |
| 545 | // to actually do anything. |
| 546 | bool hasRef() { |
| 547 | return false; |
| 548 | } |
| 549 | void ref() { /* non-op */ } |
| 550 | void unref() { /* non-op */ } |
| 551 | |
| 552 | void dispose(); |
| 553 | |
| 554 | JSG_RESOURCE_TYPE(Immediate) { |
| 555 | JSG_METHOD(ref); |
| 556 | JSG_METHOD(unref); |
| 557 | JSG_METHOD(hasRef); |
| 558 | JSG_DISPOSE(dispose); |
| 559 | } |
| 560 | |
| 561 | private: |
| 562 | // Note: We cannot use IoContext::WeakRef here because it's not thread-safe (it's only intended |
| 563 | // to be held from KJ I/O objects, but this is a JSG object which can be accessed by V8's GC |
| 564 | // on different threads). Instead, we use IoPtr<IoContext> which is safe to hold from JSG objects. |
| 565 | IoPtr<IoContext> ioContext; |
| 566 | TimeoutId timeoutId; |
| 567 | }; |
| 568 | |
| 569 | // Global object API exposed to JavaScript. |
| 570 | class ServiceWorkerGlobalScope: public WorkerGlobalScope { |
| 571 | public: |
| 572 | ServiceWorkerGlobalScope(); |
| 573 | |
| 574 | // Drop all references to JavaScript objects so that the context can be garbage-collected. Call |
| 575 | // this when the context will never be used again and should be disposed. |
| 576 | void clear(); |
| 577 | // TODO(someday): We should instead implement V8's GC visitor interface so that we don't have |
| 578 | // to hold persistent references. |
| 579 | |
| 580 | // Received request (called from C++, not JS). |
| 581 | // |
| 582 | // If `exportedHandler` is provided, the request will be delivered to it rather than to event |
| 583 | // listeners. |
| 584 | kj::Promise<DeferredProxy<void>> request(kj::HttpMethod method, |
| 585 | kj::StringPtr url, |
| 586 | const kj::HttpHeaders& headers, |
| 587 | kj::AsyncInputStream& requestBody, |
| 588 | kj::HttpService::Response& response, |
| 589 | kj::Maybe<kj::StringPtr> cfBlobJson, |
| 590 | Worker::Lock& lock, |
| 591 | kj::Maybe<ExportedHandler&> exportedHandler, |
| 592 | kj::Maybe<jsg::Ref<AbortSignal>> abortSignal); |
| 593 | // TODO(cleanup): Factor out the shared code used between old-style event listeners vs. module |
| 594 | // exports and move that code somewhere more appropriate. |
| 595 | |
| 596 | // Received TCP/socket ingress (called from C++, not JS). |
| 597 | kj::Promise<void> connect(kj::String host, |
| 598 | const kj::HttpHeaders& headers, |
| 599 | kj::AsyncIoStream& connection, |
| 600 | kj::HttpService::ConnectResponse& response, |
| 601 | Worker::Lock& lock, |
| 602 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 603 | |
| 604 | // Received sendTraces (called from C++, not JS). |
| 605 | void sendTraces(kj::ArrayPtr<kj::Own<Trace>> traces, |
| 606 | Worker::Lock& lock, |
| 607 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 608 | |
| 609 | // Start a scheduled event (called from C++, not JS). It is the caller's responsibility to wait |
| 610 | // for waitUntil()s in order to construct the final ScheduledResult. |
| 611 | void startScheduled(kj::Date scheduledTime, |
| 612 | kj::StringPtr cron, |
| 613 | Worker::Lock& lock, |
| 614 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 615 | |
| 616 | // Received runAlarm (called from C++, not JS). |
| 617 | kj::Promise<WorkerInterface::AlarmResult> runAlarm(kj::Date scheduledTime, |
| 618 | kj::Duration timeout, |
| 619 | uint32_t retryCount, |
| 620 | Worker::Lock& lock, |
| 621 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 622 | |
| 623 | // Received test() (called from C++, not JS). See WorkerInterface::test(). This version returns |
| 624 | // a jsg::Promise<void>; it fails if an exception is thrown. WorkerEntrypoint will catch these |
| 625 | // and report them. |
| 626 | jsg::Promise<void> test(Worker::Lock& lock, kj::Maybe<ExportedHandler&> exportedHandler); |
| 627 | |
| 628 | kj::Promise<void> eventTimeoutPromise(uint32_t timeoutMs); |
| 629 | kj::Promise<void> setHibernatableEventTimeout( |
| 630 | kj::Promise<void> event, kj::Maybe<uint32_t> eventTimeoutMs); |
| 631 | |
| 632 | void sendHibernatableWebSocketMessage(IoContext& context, |
| 633 | kj::OneOf<kj::String, kj::Array<byte>> message, |
| 634 | kj::Maybe<uint32_t> eventTimeoutMs, |
| 635 | kj::String websocketId, |
| 636 | Worker::Lock& lock, |
| 637 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 638 | |
| 639 | void sendHibernatableWebSocketClose(IoContext& context, |
| 640 | HibernatableSocketParams::Close close, |
| 641 | kj::Maybe<uint32_t> eventTimeoutMs, |
| 642 | kj::String websocketId, |
| 643 | Worker::Lock& lock, |
| 644 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 645 | |
| 646 | void sendHibernatableWebSocketError(IoContext& context, |
| 647 | kj::Exception e, |
| 648 | kj::Maybe<uint32_t> eventTimeoutMs, |
| 649 | kj::String websocketId, |
| 650 | Worker::Lock& lock, |
| 651 | kj::Maybe<ExportedHandler&> exportedHandler); |
| 652 | |
| 653 | void emitPromiseRejection(jsg::Lock& js, |
| 654 | v8::PromiseRejectEvent event, |
| 655 | jsg::V8Ref<v8::Promise> promise, |
| 656 | jsg::Value value); |
| 657 | |
| 658 | // Track a set of address->callback overrides for which the connect(address) behavior should be |
| 659 | // overridden via callbacks rather than using the default Socket connect() logic. |
| 660 | // This is useful for allowing generic client libraries to connect to private local services using |
| 661 | // just a provided address (rather than requiring them to support being passed a binding to call |
| 662 | // binding.connect() on). |
| 663 | using ConnectFn = kj::Function<jsg::Ref<api::Socket>(jsg::Lock&)>; |
| 664 | void setConnectOverride(kj::String networkAddress, ConnectFn connectFn); |
| 665 | kj::Maybe<ConnectFn&> getConnectOverride(kj::StringPtr networkAddress); |
| 666 | |
| 667 | // --------------------------------------------------------------------------- |
| 668 | // JS API |
| 669 | |
| 670 | jsg::JsString btoa(jsg::Lock& js, jsg::JsString data); |
| 671 | jsg::JsString atob(jsg::Lock& js, kj::String data); |
| 672 | |
| 673 | void queueMicrotask(jsg::Lock& js, jsg::Function<void()> task); |
| 674 | |
| 675 | #ifdef WORKERD_FUZZILLI |
| 676 | void fuzzilli(jsg::Lock& js, jsg::Arguments<jsg::Value> args); |
| 677 | #endif |
| 678 | |
| 679 | struct StructuredCloneOptions { |
| 680 | jsg::Optional<kj::Array<jsg::JsRef<jsg::JsValue>>> transfer; |
| 681 | JSG_STRUCT(transfer); |
| 682 | JSG_STRUCT_TS_OVERRIDE(StructuredSerializeOptions); |
| 683 | }; |
| 684 | |
| 685 | jsg::JsValue structuredClone( |
| 686 | jsg::Lock& js, jsg::JsValue value, jsg::Optional<StructuredCloneOptions> options); |
| 687 | |
| 688 | TimeoutId::NumberType setTimeout(jsg::Lock& js, |
| 689 | jsg::Function<void(jsg::Arguments<jsg::Value>)> function, |
| 690 | jsg::Optional<double> msDelay, |
| 691 | jsg::Arguments<jsg::Value> args); |
| 692 | void clearTimeout(jsg::Lock& js, kj::Maybe<jsg::JsNumber> timeoutId); |
| 693 | |
| 694 | TimeoutId::NumberType setTimeoutInternal(jsg::Function<void()> function, double msDelay); |
| 695 | |
| 696 | TimeoutId::NumberType setInterval(jsg::Lock& js, |
| 697 | jsg::Function<void(jsg::Arguments<jsg::Value>)> function, |
| 698 | jsg::Optional<double> msDelay, |
| 699 | jsg::Arguments<jsg::Value> args); |
| 700 | void clearInterval(jsg::Lock& js, kj::Maybe<jsg::JsNumber> timeoutId); |
| 701 | |
| 702 | jsg::Promise<jsg::Ref<Response>> fetch(jsg::Lock& js, |
| 703 | kj::OneOf<jsg::Ref<Request>, kj::String> request, |
| 704 | jsg::Optional<Request::Initializer> requestInitr); |
| 705 | |
| 706 | jsg::Ref<ServiceWorkerGlobalScope> getSelf() { |
| 707 | return JSG_THIS; |
| 708 | } |
| 709 | |
| 710 | // Implemented in global-scope.c++ to avoid including crypto.h |
| 711 | jsg::Ref<Crypto> getCrypto(jsg::Lock& js); |
| 712 | |
| 713 | jsg::Ref<Scheduler> getScheduler(jsg::Lock& js) { |
| 714 | return js.alloc<Scheduler>(); |
| 715 | } |
| 716 | |
| 717 | jsg::Ref<Navigator> getNavigator(jsg::Lock& js) { |
| 718 | return js.alloc<Navigator>(); |
| 719 | } |
| 720 | |
| 721 | jsg::Ref<Performance> getPerformance(jsg::Lock& js) { |
| 722 | return js.alloc<Performance>(Worker::Isolate::from(js).getLimitEnforcer()); |
| 723 | } |
| 724 | |
| 725 | jsg::Ref<Cloudflare> getCloudflare(jsg::Lock& js) { |
| 726 | return js.alloc<Cloudflare>(); |
| 727 | } |
| 728 | |
| 729 | // The origin is unknown, return "null" as described in |
| 730 | // https://html.spec.whatwg.org/multipage/browsers.html#concept-origin-opaque. |
| 731 | kj::StringPtr getOrigin() { |
| 732 | return "null"; |
| 733 | } |
| 734 | |
| 735 | jsg::Ref<CacheStorage> getCaches(jsg::Lock& js); |
| 736 | |
| 737 | void reportError(jsg::Lock& js, jsg::JsValue error); |
| 738 | |
| 739 | // When the nodejs_compat_v2 compatibility flag is enabled, we expose the Node.js |
| 740 | // compat Buffer and process at the global scope in all modules as lazy instance |
| 741 | // properties. |
| 742 | jsg::JsValue getBuffer(jsg::Lock& js); |
| 743 | void setBuffer(jsg::Lock& js, jsg::JsValue newBuffer); |
| 744 | jsg::JsValue getProcess(jsg::Lock& js); |
| 745 | void setProcess(jsg::Lock& js, jsg::JsValue newProcess); |
| 746 | jsg::Ref<Immediate> setImmediate(jsg::Lock& js, |
| 747 | jsg::Function<void(jsg::Arguments<jsg::Value>)> function, |
| 748 | jsg::Arguments<jsg::Value> args); |
| 749 | void clearImmediate(kj::Maybe<jsg::Ref<Immediate>> immediate); |
| 750 | |
| 751 | JSG_RESOURCE_TYPE(ServiceWorkerGlobalScope, CompatibilityFlags::Reader flags) { |
| 752 | JSG_INHERIT(WorkerGlobalScope); |
| 753 | |
| 754 | // *** WARNING ***: *Every* new export here must be treated as a potentially |
| 755 | // breaking change. It doesn't matter if it's a new method, a new property, |
| 756 | // or a new nested type. Adding anything to the global scope risks breaking |
| 757 | // existing user code that is feature-sniffing or monkeypatching the global. |
| 758 | // *Always* add new exports behind a new compatibility flag! And when in |
| 759 | // doubt, don't add the new export on globalThis at all. The only things |
| 760 | // that should be exported on globalThis should be standardized web APIs or |
| 761 | // Node.js compat mode globals. |
| 762 | |
| 763 | JSG_NESTED_TYPE(DOMException); |
| 764 | JSG_NESTED_TYPE(WorkerGlobalScope); |
| 765 | if (flags.getSpecCompliantPropertyAttributes()) { |
| 766 | // EventTarget is also declared on WorkerGlobalScope, but V8's |
| 767 | // FunctionTemplate::Inherit() does not propagate instance-template |
| 768 | // properties. Redeclare here so it becomes an own property of globalThis. |
| 769 | JSG_NESTED_TYPE(EventTarget); |
| 770 | } |
| 771 | |
| 772 | JSG_METHOD(btoa); |
| 773 | JSG_METHOD(atob); |
| 774 | |
| 775 | JSG_METHOD(setTimeout); |
| 776 | JSG_METHOD(clearTimeout); |
| 777 | JSG_METHOD(setInterval); |
| 778 | JSG_METHOD(clearInterval); |
| 779 | JSG_METHOD(queueMicrotask); |
| 780 | JSG_METHOD(structuredClone); |
| 781 | JSG_METHOD(reportError); |
| 782 | |
| 783 | JSG_METHOD(fetch); |
| 784 | |
| 785 | // Unlike regular interface attributes, which Web IDL requires us to |
| 786 | // implement as prototype properties, the global scope is special -- |
| 787 | // interface attributes defined on the global scope must be implemented as |
| 788 | // instance properties. As an additional wrinkle, many of these properties |
| 789 | // are supposed to be readonly, but in practice most browsers do not fully |
| 790 | // honor that part of the spec, and allow user scripts to override many of |
| 791 | // the properties. |
| 792 | // |
| 793 | // Using JSG_LAZY_INSTANCE_PROPERTY here to expose new global properties |
| 794 | // ensures that any new global property we expose can be monkeypatched by |
| 795 | // user code without us having to handle any of the storage. The |
| 796 | // first time the properties are accessed, the getter will be invoked |
| 797 | // if the user has not already set the value for the property themselves. |
| 798 | // This should be the default choice for all new global properties that |
| 799 | // are not methods or nested types. |
| 800 | // |
| 801 | // We make an exception for origin, and define it as a readonly instance |
| 802 | // property, because we currently do not provide any implementation for it. |
| 803 | |
| 804 | JSG_LAZY_INSTANCE_PROPERTY(self, getSelf); |
| 805 | JSG_LAZY_INSTANCE_PROPERTY(crypto, getCrypto); |
| 806 | JSG_LAZY_INSTANCE_PROPERTY(caches, getCaches); |
| 807 | JSG_LAZY_INSTANCE_PROPERTY(scheduler, getScheduler); |
| 808 | JSG_LAZY_INSTANCE_PROPERTY(performance, getPerformance); |
| 809 | JSG_LAZY_INSTANCE_PROPERTY(Cloudflare, getCloudflare); |
| 810 | JSG_READONLY_INSTANCE_PROPERTY(origin, getOrigin); |
| 811 | |
| 812 | #ifdef WORKERD_FUZZILLI |
| 813 | if (flags.getWorkerdExperimental()) { |
| 814 | JSG_METHOD(fuzzilli); |
| 815 | } |
| 816 | #endif |
| 817 | |
| 818 | JSG_NESTED_TYPE(Event); |
| 819 | JSG_NESTED_TYPE(ExtendableEvent); |
| 820 | JSG_NESTED_TYPE(CustomEvent); |
| 821 | JSG_NESTED_TYPE(PromiseRejectionEvent); |
| 822 | JSG_NESTED_TYPE(FetchEvent); |
| 823 | JSG_NESTED_TYPE(TailEvent); |
| 824 | JSG_NESTED_TYPE_NAMED(TailEvent, TraceEvent); |
| 825 | JSG_NESTED_TYPE(ScheduledEvent); |
| 826 | JSG_NESTED_TYPE(MessageEvent); |
| 827 | JSG_NESTED_TYPE(CloseEvent); |
| 828 | JSG_NESTED_TYPE(ReadableStreamDefaultReader); |
| 829 | JSG_NESTED_TYPE(ReadableStreamBYOBReader); |
| 830 | JSG_NESTED_TYPE(ReadableStream); |
| 831 | JSG_NESTED_TYPE(WritableStream); |
| 832 | JSG_NESTED_TYPE(WritableStreamDefaultWriter); |
| 833 | JSG_NESTED_TYPE(TransformStream); |
| 834 | JSG_NESTED_TYPE(ByteLengthQueuingStrategy); |
| 835 | JSG_NESTED_TYPE(CountQueuingStrategy); |
| 836 | JSG_NESTED_TYPE(ErrorEvent); |
| 837 | |
| 838 | if (flags.getExposeGlobalMessageChannel()) { |
| 839 | JSG_NESTED_TYPE(MessageChannel); |
| 840 | JSG_NESTED_TYPE(MessagePort); |
| 841 | } |
| 842 | |
| 843 | if (flags.getWebFileSystem()) { |
| 844 | JSG_NESTED_TYPE(FileSystemHandle); |
| 845 | JSG_NESTED_TYPE(FileSystemFileHandle); |
| 846 | JSG_NESTED_TYPE(FileSystemDirectoryHandle); |
| 847 | JSG_NESTED_TYPE(FileSystemWritableFileStream); |
| 848 | JSG_NESTED_TYPE(StorageManager); |
| 849 | } |
| 850 | |
| 851 | JSG_NESTED_TYPE(EventSource); |
| 852 | |
| 853 | if (flags.getStreamsJavaScriptControllers()) { |
| 854 | JSG_NESTED_TYPE(ReadableStreamBYOBRequest); |
| 855 | JSG_NESTED_TYPE(ReadableStreamDefaultController); |
| 856 | JSG_NESTED_TYPE(ReadableByteStreamController); |
| 857 | JSG_NESTED_TYPE(WritableStreamDefaultController); |
| 858 | JSG_NESTED_TYPE(TransformStreamDefaultController); |
| 859 | } |
| 860 | |
| 861 | if (flags.getNodeJsCompatV2()) { |
| 862 | JSG_INSTANCE_PROPERTY(Buffer, getBuffer, setBuffer); |
| 863 | JSG_INSTANCE_PROPERTY(process, getProcess, setProcess); |
| 864 | JSG_LAZY_INSTANCE_PROPERTY(global, getSelf); |
| 865 | JSG_METHOD(setImmediate); |
| 866 | JSG_METHOD(clearImmediate); |
| 867 | } |
| 868 | |
| 869 | JSG_NESTED_TYPE(CompressionStream); |
| 870 | JSG_NESTED_TYPE(DecompressionStream); |
| 871 | JSG_NESTED_TYPE(TextEncoderStream); |
| 872 | JSG_NESTED_TYPE(TextDecoderStream); |
| 873 | |
| 874 | JSG_NESTED_TYPE(Headers); |
| 875 | JSG_NESTED_TYPE(Body); |
| 876 | JSG_NESTED_TYPE(Request); |
| 877 | JSG_NESTED_TYPE(Response); |
| 878 | JSG_NESTED_TYPE(WebSocket); |
| 879 | JSG_NESTED_TYPE(WebSocketPair); |
| 880 | JSG_NESTED_TYPE(WebSocketRequestResponsePair); |
| 881 | |
| 882 | JSG_NESTED_TYPE(AbortController); |
| 883 | JSG_NESTED_TYPE(AbortSignal); |
| 884 | |
| 885 | JSG_NESTED_TYPE(TextDecoder); |
| 886 | JSG_NESTED_TYPE(TextEncoder); |
| 887 | |
| 888 | if (flags.getGlobalNavigator()) { |
| 889 | JSG_LAZY_INSTANCE_PROPERTY(navigator, getNavigator); |
| 890 | JSG_NESTED_TYPE(Navigator); |
| 891 | } |
| 892 | |
| 893 | if (flags.getSpecCompliantUrl()) { |
| 894 | JSG_NESTED_TYPE_NAMED(url::URL, URL); |
| 895 | JSG_NESTED_TYPE_NAMED(url::URLSearchParams, URLSearchParams); |
| 896 | } else { |
| 897 | JSG_NESTED_TYPE(URL); |
| 898 | JSG_NESTED_TYPE(URLSearchParams); |
| 899 | } |
| 900 | |
| 901 | if (flags.getSpecCompliantUrlpattern()) { |
| 902 | JSG_NESTED_TYPE_NAMED(urlpattern::URLPattern, URLPattern); |
| 903 | } else { |
| 904 | JSG_NESTED_TYPE(URLPattern); |
| 905 | } |
| 906 | |
| 907 | JSG_NESTED_TYPE(Blob); |
| 908 | JSG_NESTED_TYPE(File); |
| 909 | JSG_NESTED_TYPE(FormData); |
| 910 | |
| 911 | JSG_NESTED_TYPE(Crypto); |
| 912 | JSG_NESTED_TYPE(SubtleCrypto); |
| 913 | JSG_NESTED_TYPE(CryptoKey); |
| 914 | |
| 915 | JSG_NESTED_TYPE(CacheStorage); |
| 916 | JSG_NESTED_TYPE(Cache); |
| 917 | |
| 918 | // Off-spec extensions. |
| 919 | JSG_NESTED_TYPE(FixedLengthStream); |
| 920 | JSG_NESTED_TYPE(IdentityTransformStream); |
| 921 | JSG_NESTED_TYPE(HTMLRewriter); |
| 922 | |
| 923 | // Performance API |
| 924 | if (flags.getEnableGlobalPerformanceClasses() || flags.getEnableNodeJsPerfHooksModule()) { |
| 925 | JSG_NESTED_TYPE(Performance); |
| 926 | JSG_NESTED_TYPE(PerformanceEntry); |
| 927 | JSG_NESTED_TYPE(PerformanceMark); |
| 928 | JSG_NESTED_TYPE(PerformanceMeasure); |
| 929 | JSG_NESTED_TYPE(PerformanceResourceTiming); |
| 930 | JSG_NESTED_TYPE(PerformanceObserver); |
| 931 | JSG_NESTED_TYPE(PerformanceObserverEntryList); |
| 932 | } |
| 933 | |
| 934 | JSG_TS_ROOT(); |
| 935 | // JSG_TS_DEFINE_LITERAL is used here instead of JSG_TS_DEFINE because the TypeScript definition |
| 936 | // contains the `module` keyword, which Clang rejects as a C++20 module directive when it |
| 937 | // appears inside macro arguments. |
| 938 | JSG_TS_DEFINE_LITERAL(R"( |
| 939 | interface Console { |
| 940 | "assert"(condition?: boolean, ...data: any[]): void; |
| 941 | clear(): void; |
| 942 | count(label?: string): void; |
| 943 | countReset(label?: string): void; |
| 944 | debug(...data: any[]): void; |
| 945 | dir(item?: any, options?: any): void; |
| 946 | dirxml(...data: any[]): void; |
| 947 | error(...data: any[]): void; |
| 948 | group(...data: any[]): void; |
| 949 | groupCollapsed(...data: any[]): void; |
| 950 | groupEnd(): void; |
| 951 | info(...data: any[]): void; |
| 952 | log(...data: any[]): void; |
| 953 | table(tabularData?: any, properties?: string[]): void; |
| 954 | time(label?: string): void; |
| 955 | timeEnd(label?: string): void; |
| 956 | timeLog(label?: string, ...data: any[]): void; |
| 957 | timeStamp(label?: string): void; |
| 958 | trace(...data: any[]): void; |
| 959 | warn(...data: any[]): void; |
| 960 | } |
| 961 | const console: Console; |
| 962 | |
| 963 | type BufferSource = ArrayBufferView | ArrayBuffer; |
| 964 | type TypedArray = |
| 965 | | Int8Array |
| 966 | | Uint8Array |
| 967 | | Uint8ClampedArray |
| 968 | | Int16Array |
| 969 | | Uint16Array |
| 970 | | Int32Array |
| 971 | | Uint32Array |
| 972 | | Float32Array |
| 973 | | Float64Array |
| 974 | | BigInt64Array |
| 975 | | BigUint64Array; |
| 976 | |
| 977 | namespace WebAssembly { |
| 978 | class CompileError extends Error { |
| 979 | constructor(message?: string); |
| 980 | } |
| 981 | class RuntimeError extends Error { |
| 982 | constructor(message?: string); |
| 983 | } |
| 984 | |
| 985 | type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; |
| 986 | interface GlobalDescriptor { |
| 987 | value: ValueType; |
| 988 | mutable?: boolean; |
| 989 | } |
| 990 | class Global { |
| 991 | constructor(descriptor: GlobalDescriptor, value?: any); |
| 992 | value: any; |
| 993 | valueOf(): any; |
| 994 | } |
| 995 | |
| 996 | type ImportValue = ExportValue | number; |
| 997 | type ModuleImports = Record<string, ImportValue>; |
| 998 | type Imports = Record<string, ModuleImports>; |
| 999 | type ExportValue = Function | Global | Memory | Table; |
| 1000 | type Exports = Record<string, ExportValue>; |
| 1001 | class Instance { |
| 1002 | constructor(module: Module, imports?: Imports); |
| 1003 | readonly exports: Exports; |
| 1004 | } |
| 1005 | |
| 1006 | interface MemoryDescriptor { |
| 1007 | initial: number; |
| 1008 | maximum?: number; |
| 1009 | shared?: boolean; |
| 1010 | } |
| 1011 | class Memory { |
| 1012 | constructor(descriptor: MemoryDescriptor); |
| 1013 | readonly buffer: ArrayBuffer; |
| 1014 | grow(delta: number): number; |
| 1015 | } |
| 1016 | |
| 1017 | type ImportExportKind = "function" | "global" | "memory" | "table"; |
| 1018 | interface ModuleExportDescriptor { |
| 1019 | kind: ImportExportKind; |
| 1020 | name: string; |
| 1021 | } |
| 1022 | interface ModuleImportDescriptor { |
| 1023 | kind: ImportExportKind; |
| 1024 | module: string; |
| 1025 | name: string; |
| 1026 | } |
| 1027 | abstract class Module { |
| 1028 | static customSections(module: Module, sectionName: string): ArrayBuffer[]; |
| 1029 | static exports(module: Module): ModuleExportDescriptor[]; |
| 1030 | static imports(module: Module): ModuleImportDescriptor[]; |
| 1031 | } |
| 1032 | |
| 1033 | type TableKind = "anyfunc" | "externref"; |
| 1034 | interface TableDescriptor { |
| 1035 | element: TableKind; |
| 1036 | initial: number; |
| 1037 | maximum?: number; |
| 1038 | } |
| 1039 | class Table { |
| 1040 | constructor(descriptor: TableDescriptor, value?: any); |
| 1041 | readonly length: number; |
| 1042 | get(index: number): any; |
| 1043 | grow(delta: number, value?: any): number; |
| 1044 | set(index: number, value?: any): void; |
| 1045 | } |
| 1046 | |
| 1047 | function instantiate(module: Module, imports?: Imports): Promise<Instance>; |
| 1048 | function validate(bytes: BufferSource): boolean; |
| 1049 | } |
| 1050 | )"); |
| 1051 | // workerd disables dynamic WebAssembly compilation, so `compile()`, `compileStreaming()`, the |
| 1052 | // `instantiate()` override taking a `BufferSource` and `instantiateStreaming()` are omitted. |
| 1053 | // `Module` is also declared `abstract` to disable its `BufferSource` constructor. |
| 1054 | |
| 1055 | JSG_TS_OVERRIDE({ |
| 1056 | setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; |
| 1057 | setTimeout<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; |
| 1058 | |
| 1059 | setInterval(callback: (...args: any[]) => void, msDelay?: number): number; |
| 1060 | setInterval<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; |
| 1061 | |
| 1062 | structuredClone<T>(value: T, options?: StructuredSerializeOptions): T; |
| 1063 | queueMicrotask(task: Function): void; |
| 1064 | |
| 1065 | fetch(input: RequestInfo | URL, init?: RequestInit<RequestInitCfProperties>): Promise<Response>; |
| 1066 | }); |
| 1067 | } |
| 1068 | |
| 1069 | TimeoutId::Generator timeoutIdGenerator; |
| 1070 | // The generator for all timeout IDs associated with this scope. |
| 1071 | |
| 1072 | void visitForMemoryInfo(jsg::MemoryTracker& tracker) const { |
| 1073 | tracker.trackField("unhandledRejections", unhandledRejections); |
| 1074 | } |
| 1075 | |
| 1076 | private: |
| 1077 | jsg::UnhandledRejectionHandler unhandledRejections; |
| 1078 | kj::Maybe<jsg::JsRef<jsg::JsValue>> processValue; |
| 1079 | kj::Maybe<jsg::JsRef<jsg::JsValue>> bufferValue; |
| 1080 | kj::Maybe<jsg::Ref<Fetcher>> defaultFetcher; |
| 1081 | kj::HashMap<kj::String, ConnectFn> connectOverrides; |
| 1082 | |
| 1083 | // Global properties such as scheduler, crypto, caches, self, and origin should |
| 1084 | // be monkeypatchable / mutable at the global scope. |
| 1085 | }; |
| 1086 | |
| 1087 | #define EW_GLOBAL_SCOPE_ISOLATE_TYPES \ |
| 1088 | api::WorkerGlobalScope, api::ServiceWorkerGlobalScope, api::TestController, \ |
| 1089 | api::ExecutionContext, api::ExportedHandler, \ |
| 1090 | api::ServiceWorkerGlobalScope::StructuredCloneOptions, api::Navigator, \ |
| 1091 | api::AlarmInvocationInfo, api::Immediate, api::Cloudflare, api::CachePurgeError, \ |
| 1092 | api::CachePurgeResult, api::CachePurgeOptions, api::CacheContext |
| 1093 | // The list of global-scope.h types that are added to worker.c++'s JSG_DECLARE_ISOLATE_TYPE |
| 1094 | } // namespace workerd::api |