Skip to content

Commit 55a10a3

Browse files
Stabilise Worker Loader binding (#10721)
* Stabilise Worker Loader binding * add changeset * fix: resolve TypeScript errors in Worker Loader binding tests - Add missing inspector_port and container_engine properties to DevConfig in configuration test - Remove invalid schema property from logfwdr configuration - Add missing worker_loaders property to type generation test mock - Add missing Environment properties: keep_names, assets, observability, compliance_region, images Co-Authored-By: [email protected] <[email protected]> * feat: add WorkerLoader type generation support - Add missing worker_loaders property to configToDTS object - Add test case with worker loader bindings to verify type generation - Update test snapshots to include expected WorkerLoader type output Follows the same pattern as rate limit bindings for consistent implementation. Co-Authored-By: [email protected] <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 97a72cc commit 55a10a3

File tree

19 files changed

+228
-18
lines changed

19 files changed

+228
-18
lines changed

.changeset/witty-ideas-rhyme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Stabilise Worker Loader bindings

fixtures/dynamic-worker-loading/wrangler.jsonc

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
"name": "dynamic-worker-loading",
33
"main": "src/index.ts",
44
"compatibility_date": "2023-05-04",
5-
"unsafe": {
6-
"bindings": [
7-
{
8-
"name": "LOADER",
9-
"type": "worker-loader",
10-
},
11-
],
12-
},
5+
"worker_loaders": [
6+
{
7+
"binding": "LOADER",
8+
},
9+
],
1310
}

packages/miniflare/src/plugins/worker-loader/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { z } from "zod";
22
import { Worker_Binding } from "../../runtime";
33
import { Plugin } from "../shared";
44

5-
export const WorkerLoaderConfigSchema = z.object({
6-
id: z.string().optional(),
7-
});
5+
export const WorkerLoaderConfigSchema = z.object({});
86
export const WorkerLoaderOptionsSchema = z.object({
97
workerLoaders: z.record(WorkerLoaderConfigSchema).optional(),
108
});

packages/wrangler/src/__tests__/config/configuration.test.ts

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { normalizeString } from "../helpers/normalize";
77
import { runInTempDir } from "../helpers/run-in-tmp";
88
import { writeWranglerConfig } from "../helpers/write-wrangler-config";
99
import type {
10+
Config,
1011
ConfigFields,
1112
RawConfig,
1213
RawDevConfig,
@@ -78,6 +79,8 @@ describe("normalizeAndValidateConfig()", () => {
7879
upstream_protocol: "http",
7980
host: undefined,
8081
enable_containers: true,
82+
inspector_port: undefined,
83+
container_engine: undefined,
8184
},
8285
containers: undefined,
8386
cloudchamber: {},
@@ -92,7 +95,6 @@ describe("normalizeAndValidateConfig()", () => {
9295
legacy_env: true,
9396
logfwdr: {
9497
bindings: [],
95-
schema: undefined,
9698
},
9799
send_metrics: undefined,
98100
main: undefined,
@@ -125,7 +127,6 @@ describe("normalizeAndValidateConfig()", () => {
125127
},
126128
dispatch_namespaces: [],
127129
mtls_certificates: [],
128-
usage_model: undefined,
129130
vars: {},
130131
define: {},
131132
definedEnvironments: [],
@@ -134,18 +135,30 @@ describe("normalizeAndValidateConfig()", () => {
134135
data_blobs: undefined,
135136
workers_dev: undefined,
136137
preview_urls: undefined,
137-
zone_id: undefined,
138138
no_bundle: undefined,
139139
minify: undefined,
140140
first_party_worker: undefined,
141141
keep_vars: undefined,
142142
logpush: undefined,
143143
upload_source_maps: undefined,
144144
placement: undefined,
145+
worker_loaders: [],
145146
tail_consumers: undefined,
146147
pipelines: [],
147148
workflows: [],
148-
});
149+
userConfigPath: undefined,
150+
topLevelName: undefined,
151+
alias: undefined,
152+
find_additional_modules: undefined,
153+
preserve_file_names: undefined,
154+
base_dir: undefined,
155+
limits: undefined,
156+
keep_names: undefined,
157+
assets: undefined,
158+
observability: undefined,
159+
compliance_region: undefined,
160+
images: undefined,
161+
} satisfies Config);
149162
expect(diagnostics.hasErrors()).toBe(false);
150163
expect(diagnostics.hasWarnings()).toBe(false);
151164
});
@@ -3978,6 +3991,91 @@ describe("normalizeAndValidateConfig()", () => {
39783991
});
39793992
});
39803993

3994+
describe("[worker_loaders]", () => {
3995+
it("should error if worker_loaders is an object", () => {
3996+
const { diagnostics } = normalizeAndValidateConfig(
3997+
// @ts-expect-error purposely using an invalid value
3998+
{ worker_loaders: {} },
3999+
undefined,
4000+
undefined,
4001+
{ env: undefined }
4002+
);
4003+
4004+
expect(diagnostics.hasWarnings()).toBe(false);
4005+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4006+
"Processing wrangler configuration:
4007+
- The field \\"worker_loaders\\" should be an array but got {}."
4008+
`);
4009+
});
4010+
4011+
it("should error if worker_loaders is null", () => {
4012+
const { diagnostics } = normalizeAndValidateConfig(
4013+
// @ts-expect-error purposely using an invalid value
4014+
{ worker_loaders: null },
4015+
undefined,
4016+
undefined,
4017+
{ env: undefined }
4018+
);
4019+
4020+
expect(diagnostics.hasWarnings()).toBe(false);
4021+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4022+
"Processing wrangler configuration:
4023+
- The field \\"worker_loaders\\" should be an array but got null."
4024+
`);
4025+
});
4026+
4027+
it("should accept valid bindings", () => {
4028+
const { diagnostics } = normalizeAndValidateConfig(
4029+
{
4030+
worker_loaders: [
4031+
{
4032+
binding: "VALID",
4033+
},
4034+
],
4035+
},
4036+
undefined,
4037+
undefined,
4038+
{ env: undefined }
4039+
);
4040+
4041+
expect(diagnostics.hasErrors()).toBe(false);
4042+
});
4043+
4044+
it("should error if worker_loaders bindings are not valid", () => {
4045+
const { diagnostics } = normalizeAndValidateConfig(
4046+
{
4047+
worker_loaders: [
4048+
// @ts-expect-error Test if empty object is caught
4049+
{},
4050+
{
4051+
binding: "VALID",
4052+
},
4053+
{
4054+
// @ts-expect-error Test if binding is not a string
4055+
binding: null,
4056+
invalid: true,
4057+
},
4058+
],
4059+
},
4060+
undefined,
4061+
undefined,
4062+
{ env: undefined }
4063+
);
4064+
4065+
expect(diagnostics.hasWarnings()).toBe(true);
4066+
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4067+
"Processing wrangler configuration:
4068+
- Unexpected fields found in worker_loaders[2] field: \\"invalid\\""
4069+
`);
4070+
expect(diagnostics.hasErrors()).toBe(true);
4071+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4072+
"Processing wrangler configuration:
4073+
- \\"worker_loaders[0]\\" bindings must have a string \\"binding\\" field but got {}.
4074+
- \\"worker_loaders[2]\\" bindings must have a string \\"binding\\" field but got {\\"binding\\":null,\\"invalid\\":true}."
4075+
`);
4076+
});
4077+
});
4078+
39814079
describe("[unsafe_hello_world]", () => {
39824080
it("should error if unsafe_hello_world is an object", () => {
39834081
const { diagnostics } = normalizeAndValidateConfig(

packages/wrangler/src/__tests__/type-generation.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,11 @@ const bindingsConfigMock: Omit<
263263
},
264264
},
265265
],
266+
worker_loaders: [
267+
{
268+
binding: "WORKER_LOADER_BINDING",
269+
},
270+
],
266271
};
267272

268273
describe("generate types", () => {
@@ -480,6 +485,7 @@ describe("generate types", () => {
480485
SECRET: SecretsStoreSecret;
481486
HELLO_WORLD: HelloWorldBinding;
482487
RATE_LIMITER: RateLimit;
488+
WORKER_LOADER_BINDING: WorkerLoader;
483489
SERVICE_BINDING: Fetcher /* service_name */;
484490
OTHER_SERVICE_BINDING: Service /* entrypoint FakeEntrypoint from service_name_2 */;
485491
OTHER_SERVICE_BINDING_ENTRYPOINT: Service /* entrypoint RealEntrypoint from service_name_2 */;
@@ -579,6 +585,7 @@ describe("generate types", () => {
579585
SECRET: SecretsStoreSecret;
580586
HELLO_WORLD: HelloWorldBinding;
581587
RATE_LIMITER: RateLimit;
588+
WORKER_LOADER_BINDING: WorkerLoader;
582589
SERVICE_BINDING: Fetcher /* service_name */;
583590
OTHER_SERVICE_BINDING: Service /* entrypoint FakeEntrypoint from service_name_2 */;
584591
OTHER_SERVICE_BINDING_ENTRYPOINT: Service /* entrypoint RealEntrypoint from service_name_2 */;
@@ -742,6 +749,7 @@ describe("generate types", () => {
742749
SECRET: SecretsStoreSecret;
743750
HELLO_WORLD: HelloWorldBinding;
744751
RATE_LIMITER: RateLimit;
752+
WORKER_LOADER_BINDING: WorkerLoader;
745753
SERVICE_BINDING: Service<typeof import(\\"../b/index\\").default>;
746754
OTHER_SERVICE_BINDING: Service /* entrypoint FakeEntrypoint from service_name_2 */;
747755
OTHER_SERVICE_BINDING_ENTRYPOINT: Service<typeof import(\\"../c/index\\").RealEntrypoint>;

packages/wrangler/src/api/startDevWorker/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import type {
3434
CfTailConsumer,
3535
CfUnsafe,
3636
CfVectorize,
37+
CfWorkerLoader,
3738
CfWorkflow,
3839
} from "../../deployment-bundle/worker";
3940
import type { CfAccount } from "../../dev/create-worker-preview";
@@ -306,6 +307,7 @@ export type Binding =
306307
| ({ type: "logfwdr" } & NameOmit<CfLogfwdrBinding>)
307308
| ({ type: "unsafe_hello_world" } & BindingOmit<CfHelloWorld>)
308309
| ({ type: "ratelimit" } & NameOmit<CfRateLimit>)
310+
| ({ type: "worker_loader" } & BindingOmit<CfWorkerLoader>)
309311
| { type: `unsafe_${string}` }
310312
| { type: "assets" };
311313

packages/wrangler/src/api/startDevWorker/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ export function convertCfWorkerInitBindingsToBindings(
287287
}
288288
break;
289289
}
290+
case "worker_loaders": {
291+
for (const { binding, ...x } of info) {
292+
output[binding] = { type: "worker_loader", ...x };
293+
}
294+
break;
295+
}
290296
default: {
291297
assertNever(type);
292298
}
@@ -331,6 +337,7 @@ export async function convertBindingsToCfWorkerInitBindings(
331337
pipelines: undefined,
332338
unsafe_hello_world: undefined,
333339
ratelimits: undefined,
340+
worker_loaders: undefined,
334341
};
335342

336343
const fetchers: Record<string, ServiceFetch> = {};
@@ -423,6 +430,9 @@ export async function convertBindingsToCfWorkerInitBindings(
423430
} else if (binding.type === "ratelimit") {
424431
bindings.ratelimits ??= [];
425432
bindings.ratelimits.push({ ...binding, name: name });
433+
} else if (binding.type === "worker_loader") {
434+
bindings.worker_loaders ??= [];
435+
bindings.worker_loaders.push({ ...binding, binding: name });
426436
} else if (isUnsafeBindingType(binding.type)) {
427437
bindings.unsafe ??= {
428438
bindings: [],

packages/wrangler/src/config/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ export const defaultWranglerConfig: Config = {
335335
version_metadata: undefined,
336336
unsafe_hello_world: [],
337337
ratelimits: [],
338+
worker_loaders: [],
338339

339340
/*====================================================*/
340341
/* Fields supported by Workers only */

packages/wrangler/src/config/environment.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,20 @@ export interface EnvironmentNonInheritable {
11741174
period: 10 | 60;
11751175
};
11761176
}[];
1177+
1178+
/**
1179+
* Specifies Worker Loader bindings that are bound to this Worker environment.
1180+
*
1181+
* NOTE: This field is not automatically inherited from the top level environment,
1182+
* and so must be specified in every named environment.
1183+
*
1184+
* @default []
1185+
* @nonInheritable
1186+
*/
1187+
worker_loaders: {
1188+
/** The binding name used to refer to the Worker Loader in the Worker. */
1189+
binding: string;
1190+
}[];
11771191
}
11781192

11791193
/**

packages/wrangler/src/config/validation.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,16 @@ function normalizeAndValidateEnvironment(
14541454
validateBindingArray(envName, validateHelloWorldBinding),
14551455
[]
14561456
),
1457+
worker_loaders: notInheritable(
1458+
diagnostics,
1459+
topLevelEnv,
1460+
rawConfig,
1461+
rawEnv,
1462+
envName,
1463+
"worker_loaders",
1464+
validateBindingArray(envName, validateWorkerLoaderBinding),
1465+
[]
1466+
),
14571467
ratelimits: notInheritable(
14581468
diagnostics,
14591469
topLevelEnv,
@@ -2385,6 +2395,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
23852395
"logfwdr",
23862396
"mtls_certificate",
23872397
"pipeline",
2398+
"worker-loader",
23882399
];
23892400

23902401
if (safeBindings.includes(value.type)) {
@@ -3867,6 +3878,34 @@ const validateHelloWorldBinding: ValidatorFn = (diagnostics, field, value) => {
38673878
return isValid;
38683879
};
38693880

3881+
const validateWorkerLoaderBinding: ValidatorFn = (
3882+
diagnostics,
3883+
field,
3884+
value
3885+
) => {
3886+
if (typeof value !== "object" || value === null) {
3887+
diagnostics.errors.push(
3888+
`"worker_loader" bindings should be objects, but got ${JSON.stringify(value)}`
3889+
);
3890+
return false;
3891+
}
3892+
let isValid = true;
3893+
if (!isRequiredProperty(value, "binding", "string")) {
3894+
diagnostics.errors.push(
3895+
`"${field}" bindings must have a string "binding" field but got ${JSON.stringify(
3896+
value
3897+
)}.`
3898+
);
3899+
isValid = false;
3900+
}
3901+
3902+
validateAdditionalProperties(diagnostics, field, Object.keys(value), [
3903+
"binding",
3904+
]);
3905+
3906+
return isValid;
3907+
};
3908+
38703909
const validateRateLimitBinding: ValidatorFn = (diagnostics, field, value) => {
38713910
if (typeof value !== "object" || value === null) {
38723911
diagnostics.errors.push(

0 commit comments

Comments
 (0)