Skip to content
File

Blob: src/cloudflare/workers.ts

typescript218 lines
1// Copyright (c) 2024 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// TODO(cleanup): C++ built-in modules do not yet support named exports, so we must define this
6// wrapper module that simply re-exports the classes from the built-in module.
7 
8import entrypoints from 'cloudflare-internal:workers';
9import innerEnv from 'cloudflare-internal:env';
10import innerTracing from 'cloudflare-internal:tracing';
11 
12export const WorkerEntrypoint = entrypoints.WorkerEntrypoint;
13export const DurableObject = entrypoints.DurableObject;
14export const RpcStub = entrypoints.RpcStub;
15export const RpcPromise = entrypoints.RpcPromise;
16export const RpcProperty = entrypoints.RpcProperty;
17export const RpcTarget = entrypoints.RpcTarget;
18export const ServiceStub = entrypoints.ServiceStub;
19export const WorkflowEntrypoint = entrypoints.WorkflowEntrypoint;
20 
21export function withEnv(newEnv: unknown, fn: () => unknown): unknown {
22 return innerEnv.withEnv(newEnv, fn);
23}
24 
25export function withExports(newExports: unknown, fn: () => unknown): unknown {
26 return innerEnv.withExports(newExports, fn);
27}
28 
29export function withEnvAndExports(
30 newEnv: unknown,
31 newExports: unknown,
32 fn: () => unknown
33): unknown {
34 return innerEnv.withEnvAndExports(newEnv, newExports, fn);
35}
36 
37// A proxy for the workers env/bindings. Since env is imported as a module-level
38// reference, the object identity cannot be changed. The proxy provides indirection,
39// delegating to different underlying env objects based on async context (see withEnv()).
40// Mutations via this proxy modify the current underlying env object in-place - if you're
41// inside a withEnv() scope, mutations affect the override object, not the base environment.
42export const env = new Proxy(
43 {},
44 {
45 get(_: unknown, prop: string | symbol): unknown {
46 const inner = innerEnv.getCurrentEnv();
47 if (inner) {
48 return Reflect.get(inner, prop);
49 }
50 return undefined;
51 },
52 
53 set(_: unknown, prop: string | symbol, newValue: unknown): boolean {
54 const inner = innerEnv.getCurrentEnv();
55 if (inner) {
56 return Reflect.set(inner, prop, newValue);
57 }
58 return true;
59 },
60 
61 has(_: unknown, prop: string | symbol): boolean {
62 const inner = innerEnv.getCurrentEnv();
63 if (inner) {
64 return Reflect.has(inner, prop);
65 }
66 return false;
67 },
68 
69 ownKeys(_: unknown): ArrayLike<string | symbol> {
70 const inner = innerEnv.getCurrentEnv();
71 if (inner) {
72 return Reflect.ownKeys(inner);
73 }
74 return [];
75 },
76 
77 deleteProperty(_: unknown, prop: string | symbol): boolean {
78 const inner = innerEnv.getCurrentEnv();
79 if (inner) {
80 return Reflect.deleteProperty(inner, prop);
81 }
82 return true;
83 },
84 
85 defineProperty(
86 _: unknown,
87 prop: string | symbol,
88 attr: PropertyDescriptor
89 ): boolean {
90 const inner = innerEnv.getCurrentEnv();
91 if (inner) {
92 return Reflect.defineProperty(inner, prop, attr);
93 }
94 return true;
95 },
96 
97 getOwnPropertyDescriptor(
98 _: unknown,
99 prop: string | symbol
100 ): PropertyDescriptor | undefined {
101 const inner = innerEnv.getCurrentEnv();
102 if (inner) {
103 return Reflect.getOwnPropertyDescriptor(inner, prop);
104 }
105 return undefined;
106 },
107 }
108);
109 
110// A proxy for the worker exports. Since exports is imported as a module-level
111// reference, the object identity cannot be changed. The proxy provides indirection,
112// delegating to different underlying exports objects based on async context (see
113// withExports()). This proxy is read-only - mutations are not supported.
114export const exports = new Proxy(
115 {},
116 {
117 get(_: unknown, prop: string | symbol): unknown {
118 const inner = innerEnv.getCurrentExports();
119 if (inner) {
120 return Reflect.get(inner, prop);
121 }
122 return undefined;
123 },
124 
125 has(_: unknown, prop: string | symbol): boolean {
126 const inner = innerEnv.getCurrentExports();
127 if (inner) {
128 return Reflect.has(inner, prop);
129 }
130 return false;
131 },
132 
133 ownKeys(_: unknown): ArrayLike<string | symbol> {
134 const inner = innerEnv.getCurrentExports();
135 if (inner) {
136 return Reflect.ownKeys(inner);
137 }
138 return [];
139 },
140 
141 getOwnPropertyDescriptor(
142 _: unknown,
143 prop: string | symbol
144 ): PropertyDescriptor | undefined {
145 const inner = innerEnv.getCurrentExports();
146 if (inner) {
147 return Reflect.getOwnPropertyDescriptor(inner, prop);
148 }
149 return undefined;
150 },
151 }
152);
153 
154export const waitUntil = entrypoints.waitUntil.bind(entrypoints);
155 
156// A proxy for the worker's cache context (ctx.cache). Since cache is imported as a module-level
157// reference, the object identity cannot be changed. The proxy provides indirection, delegating
158// to the current request's CacheContext. This ensures that cache remains entrypoint specific
159// ensuring that the runtime always delegates the right host to clear.
160export const cache = new Proxy(
161 {},
162 {
163 get(_: unknown, prop: string | symbol): unknown {
164 const inner = entrypoints.getCtxCache();
165 if (inner) {
166 const value: unknown = Reflect.get(inner, prop);
167 // Bind methods to the underlying CacheContext so that `this` is correct
168 // when calling e.g. cache.purge() through the proxy.
169 if (typeof value === 'function') {
170 return Function.prototype.bind.call(value, inner);
171 }
172 return value;
173 }
174 // Used to enable safe no-op access outside module init.
175 return undefined;
176 },
177 
178 has(_: unknown, prop: string | symbol): boolean {
179 const inner = entrypoints.getCtxCache();
180 if (inner) {
181 return Reflect.has(inner, prop);
182 }
183 return false;
184 },
185 
186 ownKeys(_: unknown): ArrayLike<string | symbol> {
187 const inner = entrypoints.getCtxCache();
188 if (inner) {
189 return Reflect.ownKeys(inner);
190 }
191 return [];
192 },
193 
194 getOwnPropertyDescriptor(
195 _: unknown,
196 prop: string | symbol
197 ): PropertyDescriptor | undefined {
198 const inner = entrypoints.getCtxCache();
199 if (inner) {
200 return Reflect.getOwnPropertyDescriptor(inner, prop);
201 }
202 return undefined;
203 },
204 }
205);
206 
207export const tracing = innerTracing;
208 
209export function abortIsolate(reason?: string): never {
210 if (entrypoints.isExperimental) {
211 entrypoints.abortIsolate(reason);
212 } else {
213 throw new Error(
214 'abortIsolate() requires the "experimental" compatibility flag.'
215 );
216 }
217}