Skip to content

Commit 7a54137

Browse files
Add support for URL/URLSearchParams inside the workflow sandbox (#1173)
1 parent 4c8889c commit 7a54137

File tree

8 files changed

+29
-3
lines changed

8 files changed

+29
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ packages/testing/generated-protos/
1111
packages/core-bridge/releases
1212
packages/*/package-lock.json
1313
/sdk-node.iml
14+
*~

packages/test/src/integration-tests.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
163163
t.is(res, 'Hello, world!');
164164
});
165165

166+
test('url-whatwg', async (t) => {
167+
const { client } = t.context;
168+
const res = await client.execute(workflows.urlEcho, {
169+
taskQueue: 'test',
170+
workflowId: uuid4(),
171+
args: ['http://foo.com'],
172+
});
173+
t.is(res, 'http://foo.com/?counter=1');
174+
});
175+
166176
test('cancel-fake-progress', async (t) => {
167177
const { client } = t.context;
168178
await client.execute(workflows.cancelFakeProgress, {

packages/test/src/workflows/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,6 @@ export * from './two-strings';
9191
export { isBlockedQuery, unblockOrCancel } from './unblock-or-cancel';
9292
export * from './unhandled-rejection';
9393
export * from './upsert-and-read-search-attributes';
94+
export * from './url-whatwg';
9495
export * from './wait-on-user';
9596
export * from './workflow-cancellation-scenarios';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// import URL but use directly from global URLSearchParams
2+
import { URL } from 'url';
3+
4+
export async function urlEcho(url: string): Promise<string> {
5+
const parsedURL = new URL(url);
6+
const searchParams = new URLSearchParams({ counter: '1' });
7+
parsedURL.search = searchParams.toString();
8+
return parsedURL.toString();
9+
}

packages/worker/src/workflow/bundler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { toMB } from '../utils';
1010

1111
export const defaultWorflowInterceptorModules = [require.resolve('../workflow-log-interceptor')];
1212

13-
export const allowedBuiltinModules = ['assert'];
13+
export const allowedBuiltinModules = ['assert', 'url'];
1414
export const disallowedBuiltinModules = builtinModules.filter((module) => !allowedBuiltinModules.includes(module));
1515
export const disallowedModules = [
1616
...disallowedBuiltinModules,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* eslint-disable import/unambiguous */
2+
// Only expose the WHATWG URL API
3+
module.exports = { URL, URLSearchParams };

packages/worker/src/workflow/reusable-vm.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import assert from 'node:assert';
2+
import { URL, URLSearchParams } from 'node:url';
23
import { AsyncLocalStorage } from 'node:async_hooks';
34
import vm from 'node:vm';
45
import * as internals from '@temporalio/workflow/lib/worker-interface';
@@ -90,7 +91,7 @@ export class ReusableVMWorkflowCreator implements WorkflowCreator {
9091
},
9192
}
9293
);
93-
const globals = { AsyncLocalStorage, assert, __webpack_module_cache__ };
94+
const globals = { AsyncLocalStorage, URL, URLSearchParams, assert, __webpack_module_cache__ };
9495
this._context = vm.createContext(globals, { microtaskMode: 'afterEvaluate' });
9596
this.injectConsole();
9697
script.runInContext(this.context);

packages/worker/src/workflow/vm.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import assert from 'node:assert';
2+
import { URL, URLSearchParams } from 'node:url';
23
import { AsyncLocalStorage } from 'node:async_hooks';
34
import vm from 'node:vm';
45
import { IllegalStateError } from '@temporalio/common';
@@ -76,7 +77,7 @@ export class VMWorkflowCreator implements WorkflowCreator {
7677
if (this.script === undefined) {
7778
throw new IllegalStateError('Isolate context provider was destroyed');
7879
}
79-
const globals = { AsyncLocalStorage, assert, __webpack_module_cache__: {} };
80+
const globals = { AsyncLocalStorage, URL, URLSearchParams, assert, __webpack_module_cache__: {} };
8081
const context = vm.createContext(globals, { microtaskMode: 'afterEvaluate' });
8182
this.script.runInContext(context);
8283
return context;

0 commit comments

Comments
 (0)