Skip to content
File

Blob: src/workerd/api/worker-loader.h

cpp157 lines
1#pragma once
2 
3#include <workerd/io/compatibility-date.capnp.h>
4#include <workerd/io/compatibility-date.h>
5#include <workerd/io/io-channels.h>
6#include <workerd/io/io-own.h>
7#include <workerd/io/worker.h>
8#include <workerd/jsg/setup.h>
9 
10namespace workerd::api {
11 
12class Fetcher;
13class DurableObjectClass;
14 
15// JS stub pointing to a remote Worker loaded using WorkerLoader. This is not a stub for a specific
16// entrypoint, but instead the entire Worker, allowing the caller to call any entrypoint (and
17// specify arbitrary props).
18class WorkerStub: public jsg::Object {
19 public:
20 WorkerStub(IoOwn<WorkerStubChannel> channel): channel(kj::mv(channel)) {}
21 
22 struct EntrypointOptions {
23 jsg::Optional<jsg::JsRef<jsg::JsObject>> props;
24 jsg::Optional<ResourceLimits> limits;
25 
26 JSG_STRUCT(props, limits);
27 };
28 
29 jsg::Ref<Fetcher> getEntrypoint(jsg::Lock& js,
30 jsg::Optional<kj::Maybe<kj::String>> name,
31 jsg::Optional<EntrypointOptions> options);
32 jsg::Ref<DurableObjectClass> getDurableObjectClass(jsg::Lock& js,
33 jsg::Optional<kj::Maybe<kj::String>> name,
34 jsg::Optional<EntrypointOptions> options);
35 
36 JSG_RESOURCE_TYPE(WorkerStub, CompatibilityFlags::Reader flags) {
37 JSG_METHOD(getEntrypoint);
38 JSG_METHOD(getDurableObjectClass);
39 
40 JSG_TS_OVERRIDE({
41 getEntrypoint<T extends Rpc.WorkerEntrypointBranded | undefined>(
42 name?: string, options?: WorkerStubEntrypointOptions): Fetcher<T>;
43 getDurableObjectClass<T extends Rpc.DurableObjectBranded | undefined>(
44 name?: string, options?: WorkerStubEntrypointOptions): DurableObjectClass<T>;
45 });
46 }
47 
48 private:
49 IoOwn<WorkerStubChannel> channel;
50};
51 
52// JS interface for worker loader binding.
53class WorkerLoader: public jsg::Object {
54 public:
55 // Create a WorkerLoader backed by the given I/O channel.
56 //
57 // `compatDateValidation` will differ between workerd vs. production.
58 explicit WorkerLoader(uint channel, CompatibilityDateValidation compatDateValidation)
59 : channel(channel),
60 compatDateValidation(compatDateValidation) {}
61 
62 struct Module {
63 // Exactly one must be filled in.
64 jsg::Optional<kj::String> js; // ES module
65 jsg::Optional<kj::String> cjs; // Common JS module
66 jsg::Optional<kj::String> text; // text blob, imports as a string
67 jsg::Optional<kj::Array<const byte>> data; // byte blob, imports as ArrayBuffer
68 jsg::Optional<jsg::Value> json; // arbitrary JS value, will be serialized to JSON
69 // and then parsed again when imported
70 jsg::Optional<kj::String> py; // Python module
71 jsg::Optional<kj::Array<const byte>> wasm; // compiled WASM module
72 
73 JSG_STRUCT(js, cjs, text, data, json, py, wasm);
74 
75 // HACK: When we serialize the JSON in extractSource() we need to place the owned kj::String
76 // somewhere since Worker::Script::Source only gets a kj::StringPtr.
77 kj::Maybe<kj::String> serializedJson;
78 };
79 
80 struct WorkerCode {
81 kj::String compatibilityDate;
82 jsg::Optional<kj::Array<kj::String>> compatibilityFlags;
83 jsg::Optional<bool> allowExperimental = false;
84 
85 jsg::Optional<ResourceLimits> limits;
86 
87 kj::String mainModule;
88 
89 // Modules are specified as an object mapping names to content. If the content is just a
90 // string, an ES module is assumed. If it's an object, the type of module is determined
91 // based on which property is set.
92 jsg::Dict<kj::OneOf<Module, kj::String>> modules;
93 
94 // Any RPC-serializable value!
95 jsg::Optional<jsg::JsRef<jsg::JsObject>> env;
96 
97 // `Fetcher` (e.g. service binding) representing the loaded worker's global outbound.
98 //
99 // If omitted, inherit the current worker's global outbound.
100 //
101 // If `null`, block the global outbound (all requests throw errors).
102 jsg::Optional<kj::Maybe<jsg::Ref<Fetcher>>> globalOutbound;
103 
104 // Specify tail workers.
105 jsg::Optional<kj::Array<jsg::Ref<Fetcher>>> tails;
106 jsg::Optional<kj::Array<jsg::Ref<Fetcher>>> streamingTails;
107 
108 // TODO(someday): cache API outbound?
109 
110 JSG_STRUCT(compatibilityDate,
111 compatibilityFlags,
112 allowExperimental,
113 limits,
114 mainModule,
115 modules,
116 env,
117 globalOutbound,
118 tails,
119 streamingTails);
120 };
121 
122 jsg::Ref<WorkerStub> get(
123 jsg::Lock& js, kj::Maybe<kj::String> name, jsg::Function<jsg::Promise<WorkerCode>()> getCode);
124 
125 // Shortcut for `get(null, () => code)`.
126 jsg::Ref<WorkerStub> load(jsg::Lock& js, WorkerCode code);
127 
128 JSG_RESOURCE_TYPE(WorkerLoader) {
129 JSG_METHOD(get);
130 JSG_METHOD(load);
131 
132 JSG_TS_ROOT();
133 }
134 
135 private:
136 uint channel;
137 CompatibilityDateValidation compatDateValidation;
138 
139 static DynamicWorkerSource toDynamicWorkerSource(jsg::Lock& js,
140 IoContext& ioctx,
141 CompatibilityDateValidation compatDateValidation,
142 WorkerCode code);
143 
144 static Worker::Script::Source extractSource(jsg::Lock& js, WorkerCode& code);
145 static kj::Own<CompatibilityFlags::Reader> extractCompatFlags(
146 jsg::Lock& js, WorkerCode& code, CompatibilityDateValidation compatDateValidation);
147 
148 kj::Promise<kj::Own<const Worker>> startWorker(
149 Worker::Script::Source extractedSource, CompatibilityFlags::Reader compatibilityFlags);
150};
151 
152#define EW_WORKER_LOADER_ISOLATE_TYPES \
153 api::WorkerStub, api::WorkerStub::EntrypointOptions, api::WorkerLoader, \
154 api::WorkerLoader::Module, api::WorkerLoader::WorkerCode, workerd::ResourceLimits
155 
156} // namespace workerd::api