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

Commit 2caf2ca

Browse files
authored
Support siteInclude/siteExclude options and populate __STATIC_CONTENT_MANIFEST (#383)
* Use `glob-to-regexp` for glob parsing and matching For Workers Sites, we'll need to match globs both outside and inside workers. Splitting glob parsing into `RegExp`s and matching allows us to serialise parsed `RegExp`s into worker bindings. `glob-to-regexp` is also the package wrangler uses for globs. * Support `file://` URLs in persistence options File-system persistence can still be enabled using a regular non-URL string root. Using a `file://` URL allows us to use query parameters for controlling path sanitisation. Workers Sites requires unsanitised storage as keys must exactly match paths (e.g. we want to lookup `myfile.test.txt` not `myfile_test_txt`). * Support `site{Include/Exclude}`, populate `__STATIC_CONTENT_MANIFEST` This brings Miniflare 3's Workers Sites implementation up to parity with Miniflare 2. Note as the Cache API is not yet supported, `{ cacheControl: { bypassCache: true } }` must be passed as options to `@cloudflare/kv-asset-handler`. Closes #372 * Populate `compatibilityDate` for internal services Recent open-source runtime binaries require this to be set * PR suggestions - Include full URL when storage protocol invalid - Always try parse storage persistence URLs - Status 405 for Method Not Allowed for Sites KV Namespace
1 parent a7417a7 commit 2caf2ca

File tree

10 files changed

+403
-79
lines changed

10 files changed

+403
-79
lines changed

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/tre/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@
4141
"capnp-ts": "^0.7.0",
4242
"exit-hook": "^2.2.1",
4343
"get-port": "^5.1.1",
44+
"glob-to-regexp": "^0.4.1",
4445
"kleur": "^4.1.5",
4546
"stoppable": "^1.1.0",
47+
"undici": "5.8.2",
4648
"zod": "^3.18.0"
4749
},
4850
"devDependencies": {
4951
"@types/debug": "^4.1.7",
5052
"@types/estree": "^1.0.0",
53+
"@types/glob-to-regexp": "^0.4.1",
5154
"@types/stoppable": "^1.1.1"
5255
}
5356
}

packages/tre/src/helpers.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import globToRegexp from "glob-to-regexp";
12
import { z } from "zod";
23

34
export type Awaitable<T> = T | Promise<T>;
@@ -46,7 +47,8 @@ export type MiniflareCoreErrorCode =
4647
| "ERR_MODULE_PARSE" // SyntaxError when attempting to parse/locate modules
4748
| "ERR_MODULE_STRING_SCRIPT" // Attempt to resolve module within string script
4849
| "ERR_MODULE_DYNAMIC_SPEC" // Attempted to import/require a module without a literal spec
49-
| "ERR_MODULE_RULE"; // No matching module rule for file
50+
| "ERR_MODULE_RULE" // No matching module rule for file
51+
| "ERR_PERSIST_UNSUPPORTED"; // Unsupported storage persistence protocol
5052
export class MiniflareCoreError extends MiniflareError<MiniflareCoreErrorCode> {}
5153

5254
export class HttpError extends MiniflareError<number> {
@@ -74,3 +76,61 @@ export class DeferredPromise<T> extends Promise<T> {
7476
this.reject = promiseReject!;
7577
}
7678
}
79+
80+
// Split conversion to RegExps and testing to allow RegExps to be serialised
81+
// into Workers Sites KV namespace script. This will apply filtering, before
82+
// passing back to Miniflare's loopback server for storage access.
83+
export interface MatcherRegExps {
84+
include: RegExp[];
85+
exclude: RegExp[];
86+
}
87+
export interface SerialisableMatcherRegExps {
88+
include: string[];
89+
exclude: string[];
90+
}
91+
export function globsToRegExps(globs: string[] = []): MatcherRegExps {
92+
const include: RegExp[] = [];
93+
const exclude: RegExp[] = [];
94+
// Setting `flags: "g"` removes "^" and "$" from the generated regexp,
95+
// allowing matches anywhere in the path...
96+
// (https://github.com/fitzgen/glob-to-regexp/blob/2abf65a834259c6504ed3b80e85f893f8cd99127/index.js#L123-L127)
97+
const opts: globToRegexp.Options = { globstar: true, flags: "g" };
98+
for (const glob of globs) {
99+
// ...however, we don't actually want to include the "g" flag, since it will
100+
// change `lastIndex` as paths are matched, and we want to reuse `RegExp`s.
101+
// So, reconstruct each `RegExp` without any flags.
102+
if (glob.startsWith("!")) {
103+
exclude.push(new RegExp(globToRegexp(glob.slice(1), opts), ""));
104+
} else {
105+
include.push(new RegExp(globToRegexp(glob, opts), ""));
106+
}
107+
}
108+
return { include, exclude };
109+
}
110+
// NOTE: this function will be `toString()`ed and must not have dependencies
111+
export function testRegExps(matcher: MatcherRegExps, value: string): boolean {
112+
for (const exclude of matcher.exclude) if (exclude.test(value)) return false;
113+
for (const include of matcher.include) if (include.test(value)) return true;
114+
return false;
115+
}
116+
function serialiseRegExp(regExp: RegExp): string {
117+
const str = regExp.toString();
118+
return str.substring(str.indexOf("/") + 1, str.lastIndexOf("/"));
119+
}
120+
export function serialiseRegExps(
121+
matcher: MatcherRegExps
122+
): SerialisableMatcherRegExps {
123+
return {
124+
include: matcher.include.map(serialiseRegExp),
125+
exclude: matcher.exclude.map(serialiseRegExp),
126+
};
127+
}
128+
// NOTE: this function will be `toString()`ed and must not have dependencies
129+
export function deserialiseRegExps(
130+
matcher: SerialisableMatcherRegExps
131+
): MatcherRegExps {
132+
return {
133+
include: matcher.include.map((regExp) => new RegExp(regExp)),
134+
exclude: matcher.exclude.map((regExp) => new RegExp(regExp)),
135+
};
136+
}

packages/tre/src/plugins/core/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ export const SCRIPT_CUSTOM_SERVICE = `addEventListener("fetch", (event) => {
8989
event.respondWith(${BINDING_SERVICE_LOOPBACK}.fetch(request));
9090
})`;
9191

92+
const now = new Date();
93+
const fallbackCompatibilityDate = [
94+
now.getFullYear(),
95+
(now.getMonth() + 1).toString().padStart(2, "0"),
96+
now.getDate().toString().padStart(2, "0"),
97+
].join("-");
98+
9299
export const CORE_PLUGIN: Plugin<
93100
typeof CoreOptionsSchema,
94101
typeof CoreSharedOptionsSchema
@@ -156,6 +163,7 @@ export const CORE_PLUGIN: Plugin<
156163
name: SERVICE_ENTRY,
157164
worker: {
158165
serviceWorkerScript: SCRIPT_ENTRY,
166+
compatibilityDate: "2022-09-01",
159167
bindings: serviceEntryBindings,
160168
compatibilityDate: "2022-09-01",
161169
},
@@ -170,7 +178,8 @@ export const CORE_PLUGIN: Plugin<
170178
name,
171179
worker: {
172180
...workerScript,
173-
compatibilityDate: options.compatibilityDate,
181+
compatibilityDate:
182+
options.compatibilityDate ?? fallbackCompatibilityDate,
174183
compatibilityFlags: options.compatibilityFlags,
175184
bindings: workerBindings,
176185
},
@@ -189,6 +198,7 @@ export const CORE_PLUGIN: Plugin<
189198
name: `${SERVICE_CUSTOM_PREFIX}:${name}`,
190199
worker: {
191200
serviceWorkerScript: SCRIPT_CUSTOM_SERVICE,
201+
compatibilityDate: "2022-09-01",
192202
bindings: [
193203
{
194204
name: BINDING_TEXT_CUSTOM_SERVICE,

packages/tre/src/plugins/core/modules.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import { readFileSync } from "fs";
33
import { builtinModules } from "module";
44
import path from "path";
55
import { TextDecoder, TextEncoder } from "util";
6-
import { Matcher, globsToMatcher } from "@miniflare/shared";
76
import acorn from "acorn";
87
import walk from "acorn-walk";
98
import type estree from "estree";
109
import { dim } from "kleur/colors";
1110
import { z } from "zod";
12-
import { MiniflareCoreError } from "../../helpers";
11+
import {
12+
MatcherRegExps,
13+
MiniflareCoreError,
14+
globsToRegExps,
15+
testRegExps,
16+
} from "../../helpers";
1317
import { Worker_Module } from "../../runtime";
1418

1519
const SUGGEST_BUNDLE =
@@ -55,7 +59,7 @@ const DEFAULT_MODULE_RULES: ModuleRule[] = [
5559

5660
interface CompiledModuleRule {
5761
type: ModuleRuleType;
58-
include: Matcher;
62+
include: MatcherRegExps;
5963
}
6064

6165
function compileModuleRules(rules?: ModuleRule[]) {
@@ -66,7 +70,7 @@ function compileModuleRules(rules?: ModuleRule[]) {
6670
if (finalisedTypes.has(rule.type)) continue;
6771
compiledRules.push({
6872
type: rule.type,
69-
include: globsToMatcher(rule.include),
73+
include: globsToRegExps(rule.include),
7074
});
7175
if (!rule.fallthrough) finalisedTypes.add(rule.type);
7276
}
@@ -210,7 +214,7 @@ ${dim(modulesConfig)}`;
210214

211215
// Find first matching module rule
212216
const rule = this.#compiledRules.find((rule) =>
213-
rule.include.test(identifier)
217+
testRegExps(rule.include, identifier)
214218
);
215219
if (rule === undefined) {
216220
const prefix = getResolveErrorPrefix(referencingPath);

packages/tre/src/plugins/kv/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export const KV_PLUGIN_NAME = "kv";
2+
13
export const MIN_CACHE_TTL = 60; /* 60s */
24
export const MAX_LIST_KEYS = 1000;
35
export const MAX_KEY_SIZE = 512; /* 512B */

0 commit comments

Comments
 (0)