Skip to content
File

Blob: src/workerd/api/global-scope.h

cpp1095 lines
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 
24namespace workerd::jsg {
25class DOMException;
26} // namespace workerd::jsg
27 
28namespace workerd::api {
29 
30class Tracing;
31class TailEvent;
32class Cache;
33class CacheStorage;
34class JsRpcProperty;
35class Crypto;
36class CryptoKey;
37class ErrorEvent;
38class EventSource;
39class FixedLengthStream;
40class SubtleCrypto;
41class TextDecoder;
42class TextEncoder;
43class HTMLRewriter;
44class IdentityTransformStream;
45class Response;
46class TraceItem;
47class TransformStream;
48class ScheduledController;
49class ScheduledEvent;
50class ReadableStream;
51class ReadableStreamDefaultReader;
52class ReadableStreamBYOBReader;
53class ReadableStreamBYOBRequest;
54class WritableStream;
55class WritableStreamDefaultWriter;
56class ReadableStreamBYOBRequest;
57class ReadableStreamDefaultController;
58class ReadableByteStreamController;
59class WritableStreamDefaultController;
60class CompressionStream;
61class DecompressionStream;
62class TextEncoderStream;
63class TextDecoderStream;
64class Blob;
65class File;
66class FormData;
67 
68class URLPattern;
69namespace urlpattern {
70class URLPattern;
71} // namespace urlpattern
72 
73class URL;
74class URLSearchParams;
75namespace url {
76class URL;
77class URLSearchParams;
78} // namespace url
79 
80// We need access to DOMException within this namespace so JSG_NESTED_TYPE can name it correctly.
81using DOMException = jsg::DOMException;
82 
83// A subset of the standard Navigator API.
84class 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.
139class 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 
152class 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.
196class 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.
206struct CachePurgeError {
207 int code;
208 kj::String message;
209 JSG_STRUCT(code, message);
210};
211 
212struct CachePurgeResult {
213 bool success;
214 kj::Array<CachePurgeError> errors;
215 JSG_STRUCT(success, errors);
216};
217 
218struct 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.
227class 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 
243class 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.
386class 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.
418struct 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.
537class 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.
570class 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