Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit bce7ecf

Browse files
implement [data_blobs] (#232)
This implements support for `[data_blobs]` for service workers, which lets you bind some data from a file as an ArrayBuffer in service-worker format workers. It also adds a `--data-blob` cli argument for the same.
1 parent 57dd23d commit bce7ecf

File tree

3 files changed

+79
-14
lines changed

3 files changed

+79
-14
lines changed

packages/core/src/plugins/bindings.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
RequestContext,
1313
SetupResult,
1414
getRequestContext,
15+
viewToBuffer,
1516
} from "@miniflare/shared";
1617
import dotenv from "dotenv";
1718
import { MiniflareCoreError } from "../error";
@@ -46,6 +47,7 @@ export interface BindingsOptions {
4647
globals?: Record<string, any>;
4748
wasmBindings?: Record<string, string>;
4849
textBlobBindings?: Record<string, string>;
50+
dataBlobBindings?: Record<string, string>;
4951
serviceBindings?: ServiceBindingsOptions;
5052
}
5153

@@ -175,6 +177,16 @@ export class BindingsPlugin
175177
})
176178
textBlobBindings?: Record<string, string>;
177179

180+
@Option({
181+
type: OptionType.OBJECT,
182+
typeFormat: "NAME=PATH",
183+
name: "data-blob",
184+
description: "Data blob to bind",
185+
logName: "Data Blob Bindings",
186+
fromWrangler: ({ data_blobs }) => data_blobs,
187+
})
188+
dataBlobBindings?: Record<string, string>;
189+
178190
@Option({
179191
type: OptionType.OBJECT,
180192
typeFormat: "NAME=MOUNT[@ENV]",
@@ -258,8 +270,9 @@ export class BindingsPlugin
258270
// 2) .env Variables
259271
// 3) WASM Module Bindings
260272
// 4) Text blob Bindings
261-
// 5) Service Bindings
262-
// 6) Custom Bindings
273+
// 5) Data blob Bindings
274+
// 6) Service Bindings
275+
// 7) Custom Bindings
263276

264277
const bindings: Context = {};
265278
const watch: string[] = [];
@@ -303,12 +316,23 @@ export class BindingsPlugin
303316
}
304317
}
305318

306-
// 5) Load service bindings
319+
// 5) Load data blobs from files
320+
if (this.dataBlobBindings) {
321+
// eslint-disable-next-line prefer-const
322+
for (let [name, dataPath] of Object.entries(this.dataBlobBindings)) {
323+
dataPath = path.resolve(this.ctx.rootPath, dataPath);
324+
const fileContent = await fs.readFile(dataPath);
325+
bindings[name] = viewToBuffer(fileContent);
326+
watch.push(dataPath);
327+
}
328+
}
329+
330+
// 6) Load service bindings
307331
for (const { name, service } of this.#processedServiceBindings) {
308332
bindings[name] = new Fetcher(service, this.#getServiceFetch);
309333
}
310334

311-
// 6) Copy user's arbitrary bindings
335+
// 7) Copy user's arbitrary bindings
312336
Object.assign(bindings, this.bindings);
313337

314338
return { globals: this.globals, bindings, watch };

packages/core/test/plugins/bindings.spec.ts

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
NoOpLog,
1717
PluginContext,
1818
getRequestContext,
19+
viewToBuffer,
1920
} from "@miniflare/shared";
2021
import {
2122
getObjectProperties,
@@ -41,6 +42,8 @@ const addModulePath = path.join(fixturesPath, "add.wasm");
4142
// lorem-ipsum.txt is five paragraphs of lorem ipsum nonsense text
4243
const loremIpsumPath = path.join(fixturesPath, "lorem-ipsum.txt");
4344
const loremIpsum = readFileSync(loremIpsumPath, "utf-8");
45+
// we also make a data version of it to verify aganst data blobs
46+
const loremIpsumData = viewToBuffer(readFileSync(loremIpsumPath));
4447

4548
test("BindingsPlugin: parses options from argv", (t) => {
4649
let options = parsePluginArgv(BindingsPlugin, [
@@ -62,6 +65,10 @@ test("BindingsPlugin: parses options from argv", (t) => {
6265
"TEXT1=text-blob-1.txt",
6366
"--text-blob",
6467
"TEXT2=text-blob-2.txt",
68+
"--data-blob",
69+
"DATA1=data-blob-1.bin",
70+
"--data-blob",
71+
"DATA2=data-blob-2.bin",
6572
"--service",
6673
"SERVICE1=service1",
6774
"--service",
@@ -73,6 +80,7 @@ test("BindingsPlugin: parses options from argv", (t) => {
7380
globals: { KEY3: "value3", KEY4: "value4" },
7481
wasmBindings: { MODULE1: "module1.wasm", MODULE2: "module2.wasm" },
7582
textBlobBindings: { TEXT1: "text-blob-1.txt", TEXT2: "text-blob-2.txt" },
83+
dataBlobBindings: { DATA1: "data-blob-1.bin", DATA2: "data-blob-2.bin" },
7684
serviceBindings: {
7785
SERVICE1: "service1",
7886
SERVICE2: { service: "service2", environment: "development" },
@@ -109,6 +117,10 @@ test("BindingsPlugin: parses options from wrangler config", async (t) => {
109117
TEXT1: "text-blob-1.txt",
110118
TEXT2: "text-blob-2.txt",
111119
},
120+
data_blobs: {
121+
DATA1: "data-blob-1.bin",
122+
DATA2: "data-blob-2.bin",
123+
},
112124
experimental_services: [
113125
{ name: "SERVICE1", service: "service1", environment: "development" },
114126
{ name: "SERVICE2", service: "service2", environment: "production" },
@@ -123,6 +135,7 @@ test("BindingsPlugin: parses options from wrangler config", async (t) => {
123135
globals: { KEY5: "value5", KEY6: false, KEY7: 10 },
124136
wasmBindings: { MODULE1: "module1.wasm", MODULE2: "module2.wasm" },
125137
textBlobBindings: { TEXT1: "text-blob-1.txt", TEXT2: "text-blob-2.txt" },
138+
dataBlobBindings: { DATA1: "data-blob-1.bin", DATA2: "data-blob-2.bin" },
126139
serviceBindings: {
127140
SERVICE1: { service: "service1", environment: "development" },
128141
SERVICE2: { service: "service2", environment: "production" },
@@ -156,6 +169,7 @@ test("BindingsPlugin: logs options", (t) => {
156169
globals: { KEY5: "value5", KEY6: "value6" },
157170
wasmBindings: { MODULE1: "module1.wasm", MODULE2: "module2.wasm" },
158171
textBlobBindings: { TEXT1: "text-blob-1.txt", TEXT2: "text-blob-2.txt" },
172+
dataBlobBindings: { DATA1: "data-blob-1.bin", DATA2: "data-blob-2.bin" },
159173
serviceBindings: {
160174
SERVICE1: "service1",
161175
SERVICE2: { service: "service2", environment: "development" },
@@ -168,6 +182,7 @@ test("BindingsPlugin: logs options", (t) => {
168182
"Custom Globals: KEY5, KEY6",
169183
"WASM Bindings: MODULE1, MODULE2",
170184
"Text Blob Bindings: TEXT1, TEXT2",
185+
"Data Blob Bindings: DATA1, DATA2",
171186
"Service Bindings: SERVICE1, SERVICE2",
172187
]);
173188
logs = logPluginOptions(BindingsPlugin, { envPath: true });
@@ -292,23 +307,40 @@ test("BindingsPlugin: setup: loads text blob bindings", async (t) => {
292307
t.is(result.bindings?.LOREM_IPSUM, loremIpsum);
293308
});
294309

310+
test("BindingsPlugin: setup: loads data blob bindings", async (t) => {
311+
let plugin = new BindingsPlugin(ctx, {
312+
dataBlobBindings: { BINARY_DATA: loremIpsumPath },
313+
});
314+
let result = await plugin.setup();
315+
t.deepEqual(result.bindings?.BINARY_DATA, loremIpsumData);
316+
317+
// Check resolves data blob bindings path relative to rootPath
318+
plugin = new BindingsPlugin(
319+
{ log, compat, rootPath: path.dirname(loremIpsumPath) },
320+
{ dataBlobBindings: { BINARY_DATA: "lorem-ipsum.txt" } }
321+
);
322+
result = await plugin.setup();
323+
t.deepEqual(result.bindings?.BINARY_DATA, loremIpsumData);
324+
});
325+
295326
test("BindingsPlugin: setup: loads bindings from all sources", async (t) => {
296327
// Bindings should be loaded in this order, from lowest to highest priority:
297328
// 1) Wrangler [vars]
298329
// 2) .env Variables
299330
// 3) WASM Module Bindings
300331
// 4) Text Blob Bindings
301-
// 5) Service Bindings
302-
// 6) Custom Bindings
332+
// 5) Data Blob Bindings
333+
// 6) Service Bindings
334+
// 7) Custom Bindings
303335

304336
// wranglerOptions should contain [kWranglerBindings]
305337
const wranglerOptions = parsePluginWranglerConfig(BindingsPlugin, {
306-
vars: { A: "w", B: "w", C: "w", D: "w", E: "w", F: "w" },
338+
vars: { A: "w", B: "w", C: "w", D: "w", E: "w", F: "w", G: "w" },
307339
});
308340

309341
const tmp = await useTmp(t);
310342
const envPath = path.join(tmp, ".env");
311-
await fs.writeFile(envPath, "A=env\nB=env\nC=env\nD=env\nE=env");
343+
await fs.writeFile(envPath, "A=env\nB=env\nC=env\nD=env\nE=env\nF=env");
312344

313345
const obj = { ping: "pong" };
314346
const throws = () => {
@@ -321,11 +353,18 @@ test("BindingsPlugin: setup: loads bindings from all sources", async (t) => {
321353
B: addModulePath,
322354
C: addModulePath,
323355
D: addModulePath,
356+
E: addModulePath,
324357
},
325358
textBlobBindings: {
326359
A: loremIpsumPath,
327360
B: loremIpsumPath,
328361
C: loremIpsumPath,
362+
D: loremIpsumPath,
363+
},
364+
dataBlobBindings: {
365+
A: loremIpsumPath,
366+
B: loremIpsumPath,
367+
C: loremIpsumPath,
329368
},
330369
serviceBindings: { A: throws, B: throws },
331370
bindings: { A: obj },
@@ -334,10 +373,11 @@ test("BindingsPlugin: setup: loads bindings from all sources", async (t) => {
334373
const result = await plugin.setup();
335374
assert(result.bindings);
336375

337-
t.is(result.bindings.F, "w");
338-
t.is(result.bindings.E, "env");
339-
t.true(result.bindings.D instanceof WebAssembly.Module);
340-
t.is(result.bindings.C, loremIpsum);
376+
t.is(result.bindings.G, "w");
377+
t.is(result.bindings.F, "env");
378+
t.true(result.bindings.E instanceof WebAssembly.Module);
379+
t.is(result.bindings.D, loremIpsum);
380+
t.deepEqual(result.bindings.C, loremIpsumData);
341381
t.true(result.bindings.B instanceof Fetcher);
342382
t.is(result.bindings.A, obj);
343383
});

packages/shared/src/wrangler.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ export interface WranglerEnvironmentConfig {
3434
crons?: string[];
3535
}; // inherited
3636
usage_model?: "bundled" | "unbound"; // inherited
37-
wasm_modules?: Record<string, string>; // (probably) inherited
38-
text_blobs?: Record<string, string>;
37+
wasm_modules?: Record<string, string>; // inherited
38+
text_blobs?: Record<string, string>; // inherited
39+
data_blobs?: Record<string, string>; // inherited
3940
experimental_services?: {
4041
name: string;
4142
service: string;

0 commit comments

Comments
 (0)