Initial commit (by create-cloudflare CLI)

This commit is contained in:
Jonas_Jones 2023-09-12 00:20:38 +02:00
parent 8cb86120f1
commit fff961078a
1777 changed files with 1011798 additions and 0 deletions

View file

@ -0,0 +1,128 @@
import { isRoutingRuleMatch } from "../pages-dev-util";
describe("isRoutingRuleMatch", () => {
it("should match rules referencing root level correctly", () => {
const routingRule = "/";
expect(isRoutingRuleMatch("/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeFalsy();
});
it("should match include-all rules correctly", () => {
const routingRule = "/*";
expect(isRoutingRuleMatch("/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/baz/", routingRule)).toBeTruthy();
});
it("should match `/*` suffix-ed rules correctly", () => {
let routingRule = "/foo/*";
expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foobar", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/bar/foo/baz", routingRule)).toBeFalsy();
routingRule = "/foo/bar/*";
expect(isRoutingRuleMatch("/foo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/barfoo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("baz/foo/bar", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("baz/foo/bar/", routingRule)).toBeFalsy();
});
it("should match `/` suffix-ed rules correctly", () => {
let routingRule = "/foo/";
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
routingRule = "/foo/bar/";
expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
});
it("should match `*` suffix-ed rules correctly", () => {
let routingRule = "/foo*";
expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foobar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/barfoo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/bar/foobar", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/bar/foo/baz", routingRule)).toBeFalsy();
routingRule = "/foo/bar*";
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/barfoo", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/bar/foo/barfoo", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/bar/foo/bar/baz", routingRule)).toBeFalsy();
});
it("should match rules without wildcards correctly", () => {
let routingRule = "/foo";
expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
routingRule = "/foo/bar";
expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeFalsy();
expect(isRoutingRuleMatch("/baz/foo/bar", routingRule)).toBeFalsy();
});
it("should throw an error if pathname or routing rule params are missing", () => {
// MISSING PATHNAME
expect(() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: sanity check
isRoutingRuleMatch(undefined, "/*")
).toThrow("Pathname is undefined.");
expect(() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: sanity check
isRoutingRuleMatch(null, "/*")
).toThrow("Pathname is undefined.");
expect(() => isRoutingRuleMatch("", "/*")).toThrow(
"Pathname is undefined."
);
// MISSING ROUTING RULE
expect(() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: sanity check
isRoutingRuleMatch("/foo", undefined)
).toThrow("Routing rule is undefined.");
expect(() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: sanity check
isRoutingRuleMatch("/foo", null)
).toThrow("Routing rule is undefined.");
expect(() => isRoutingRuleMatch("/foo", "")).toThrow(
"Routing rule is undefined."
);
});
});

View file

@ -0,0 +1,12 @@
// `@types/node` should be included
Buffer.from("test");
// `@types/jest` should be included
test("test");
// @ts-expect-error `@cloudflare/workers-types` should NOT be included
const _handler: ExportedHandler = {};
// @ts-expect-error `@cloudflare/workers-types` should NOT be included
new HTMLRewriter();
export {};

View file

@ -0,0 +1,8 @@
{
"extends": "@cloudflare/workers-tsconfig/tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": ["**/*.ts"],
"exclude": []
}

View file

@ -0,0 +1,30 @@
const urls = new Set();
function checkURL(request, init) {
const url =
request instanceof URL
? request
: new URL(
(typeof request === "string"
? new Request(request, init)
: request
).url
);
if (url.port && url.port !== "443" && url.protocol === "https:") {
if (!urls.has(url.toString())) {
urls.add(url.toString());
console.warn(
`WARNING: known issue with \`fetch()\` requests to custom HTTPS ports in published Workers:\n` +
` - ${url.toString()} - the custom port will be ignored when the Worker is published using the \`wrangler deploy\` command.\n`
);
}
}
}
globalThis.fetch = new Proxy(globalThis.fetch, {
apply(target, thisArg, argArray) {
const [request, init] = argArray;
checkURL(request, init);
return Reflect.apply(target, thisArg, argArray);
},
});

View file

@ -0,0 +1,17 @@
declare module "__ENTRY_POINT__" {
import type { Middleware } from "./middleware/common";
const worker: ExportedHandler & {
middleware?: Middleware[];
envWrappers: ((env: Record<string, unknown>) => Record<string, unknown>)[];
};
export default worker;
}
declare module "__KV_ASSET_HANDLER__" {
export * from "@cloudflare/kv-asset-handler";
}
declare module "__STATIC_CONTENT_MANIFEST" {
const manifest: string;
export default manifest;
}

View file

@ -0,0 +1,172 @@
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*
# wrangler project
.dev.vars
.wrangler/

View file

@ -0,0 +1,23 @@
const { unstable_dev } = require("wrangler");
describe("Worker", () => {
let worker;
beforeAll(async () => {
worker = await unstable_dev("src/index.js", {
experimental: { disableExperimentalWarning: true },
});
});
afterAll(async () => {
await worker.stop();
});
it("should return Hello World", async () => {
const resp = await worker.fetch();
if (resp) {
const text = await resp.text();
expect(text).toMatchInlineSnapshot(`"Hello World!"`);
}
});
});

View file

@ -0,0 +1,24 @@
import { unstable_dev } from "wrangler";
import { describe, expect, it, beforeAll, afterAll } from "vitest";
describe("Worker", () => {
let worker;
beforeAll(async () => {
worker = await unstable_dev("src/index.js", {
experimental: { disableExperimentalWarning: true },
});
});
afterAll(async () => {
await worker.stop();
});
it("should return Hello World", async () => {
const resp = await worker.fetch();
if (resp) {
const text = await resp.text();
expect(text).toMatchInlineSnapshot(`"Hello World!"`);
}
});
});

View file

@ -0,0 +1,25 @@
import { unstable_dev } from "wrangler";
import type { UnstableDevWorker } from "wrangler";
import { describe, expect, it, beforeAll, afterAll } from "vitest";
describe("Worker", () => {
let worker: UnstableDevWorker;
beforeAll(async () => {
worker = await unstable_dev("src/index.ts", {
experimental: { disableExperimentalWarning: true },
});
});
afterAll(async () => {
await worker.stop();
});
it("should return Hello World", async () => {
const resp = await worker.fetch();
if (resp) {
const text = await resp.text();
expect(text).toMatchInlineSnapshot(`"Hello World!"`);
}
});
});

View file

@ -0,0 +1,67 @@
export type Awaitable<T> = T | Promise<T>;
// TODO: allow dispatching more events?
export type Dispatcher = (
type: "scheduled",
init: { cron?: string }
) => Awaitable<void>;
export type IncomingRequest = Request<
unknown,
IncomingRequestCfProperties<unknown>
>;
export interface MiddlewareContext {
dispatch: Dispatcher;
next(request: IncomingRequest, env: any): Awaitable<Response>;
}
export type Middleware = (
request: IncomingRequest,
env: any,
ctx: ExecutionContext,
middlewareCtx: MiddlewareContext
) => Awaitable<Response>;
const __facade_middleware__: Middleware[] = [];
// The register functions allow for the insertion of one or many middleware,
// We register internal middleware first in the stack, but have no way of controlling
// the order that addMiddleware is run in service workers so need an internal function.
export function __facade_register__(...args: (Middleware | Middleware[])[]) {
__facade_middleware__.push(...args.flat());
}
export function __facade_registerInternal__(
...args: (Middleware | Middleware[])[]
) {
__facade_middleware__.unshift(...args.flat());
}
function __facade_invokeChain__(
request: IncomingRequest,
env: any,
ctx: ExecutionContext,
dispatch: Dispatcher,
middlewareChain: Middleware[]
): Awaitable<Response> {
const [head, ...tail] = middlewareChain;
const middlewareCtx: MiddlewareContext = {
dispatch,
next(newRequest, newEnv) {
return __facade_invokeChain__(newRequest, newEnv, ctx, dispatch, tail);
},
};
return head(request, env, ctx, middlewareCtx);
}
export function __facade_invoke__(
request: IncomingRequest,
env: any,
ctx: ExecutionContext,
dispatch: Dispatcher,
finalMiddleware: Middleware
): Awaitable<Response> {
return __facade_invokeChain__(request, env, ctx, dispatch, [
...__facade_middleware__,
finalMiddleware,
]);
}

View file

@ -0,0 +1,137 @@
// // This loads all middlewares exposed on the middleware object
// // and then starts the invocation chain.
// // The big idea is that we can add these to the middleware export dynamically
// // through wrangler, or we can potentially let users directly add them as a sort
// // of "plugin" system.
import {
Dispatcher,
Middleware,
__facade_invoke__,
__facade_register__,
} from "./common";
import worker from "__ENTRY_POINT__";
// We need to preserve all of the exports from the worker
export * from "__ENTRY_POINT__";
class __Facade_ScheduledController__ implements ScheduledController {
#noRetry: ScheduledController["noRetry"];
constructor(
readonly scheduledTime: number,
readonly cron: string,
noRetry: ScheduledController["noRetry"]
) {
this.#noRetry = noRetry;
}
noRetry() {
if (!(this instanceof __Facade_ScheduledController__)) {
throw new TypeError("Illegal invocation");
}
// Need to call native method immediately in case uncaught error thrown
this.#noRetry();
}
}
const __facade_modules_fetch__: ExportedHandlerFetchHandler = function (
request,
env,
ctx
) {
if (worker.fetch === undefined)
throw new Error("Handler does not export a fetch() function.");
return worker.fetch(request, env, ctx);
};
function getMaskedEnv(rawEnv: unknown) {
let env = rawEnv as Record<string, unknown>;
if (worker.envWrappers && worker.envWrappers.length > 0) {
for (const wrapFn of worker.envWrappers) {
env = wrapFn(env);
}
}
return env;
}
/**
* This type is here to cause a type error if a new export handler is added to
* `ExportHandler` without it being included in the `facade` below.
*/
type MissingExportHandlers = Omit<
Required<ExportedHandler>,
"tail" | "trace" | "scheduled" | "queue" | "test" | "email" | "fetch"
>;
let registeredMiddleware = false;
const facade: ExportedHandler<unknown> & MissingExportHandlers = {
...(worker.tail && {
tail: maskHandlerEnv(worker.tail),
}),
...(worker.trace && {
trace: maskHandlerEnv(worker.trace),
}),
...(worker.scheduled && {
scheduled: maskHandlerEnv(worker.scheduled),
}),
...(worker.queue && {
queue: maskHandlerEnv(worker.queue),
}),
...(worker.test && {
test: maskHandlerEnv(worker.test),
}),
...(worker.email && {
email: maskHandlerEnv(worker.email),
}),
fetch(request, rawEnv, ctx) {
const env = getMaskedEnv(rawEnv);
// Get the chain of middleware from the worker object
if (worker.middleware && worker.middleware.length > 0) {
// Make sure we only register middleware once:
// https://github.com/cloudflare/workers-sdk/issues/2386#issuecomment-1614715911
if (!registeredMiddleware) {
registeredMiddleware = true;
for (const middleware of worker.middleware) {
__facade_register__(middleware);
}
}
const __facade_modules_dispatch__: Dispatcher = function (type, init) {
if (type === "scheduled" && worker.scheduled !== undefined) {
const controller = new __Facade_ScheduledController__(
Date.now(),
init.cron ?? "",
() => {}
);
return worker.scheduled(controller, env, ctx);
}
};
return __facade_invoke__(
request,
env,
ctx,
__facade_modules_dispatch__,
__facade_modules_fetch__
);
} else {
// We didn't have any middleware so we can skip the invocation chain,
// and just call the fetch handler directly
// We "don't care" if this is undefined as we want to have the same behavior
// as if the worker completely bypassed middleware.
return __facade_modules_fetch__(request, env, ctx);
}
},
};
type HandlerFn<D, R> = (data: D, env: unknown, ctx: ExecutionContext) => R;
function maskHandlerEnv<D, R>(handler: HandlerFn<D, R>): HandlerFn<D, R> {
return (data, env, ctx) => handler(data, getMaskedEnv(env), ctx);
}
export default facade;

View file

@ -0,0 +1,228 @@
import {
Awaitable,
Dispatcher,
IncomingRequest,
Middleware,
__facade_invoke__,
__facade_register__,
__facade_registerInternal__,
} from "./common";
export { __facade_register__, __facade_registerInternal__ };
// Miniflare 2's `EventTarget` follows the spec and doesn't allow exceptions to
// be caught by `dispatchEvent`. Instead it has a custom `ThrowingEventTarget`
// class that rethrows errors from event listeners in `dispatchEvent`.
// We'd like errors to be propagated to the top-level `addEventListener`, so
// we'd like to use `ThrowingEventTarget`. Unfortunately, `ThrowingEventTarget`
// isn't exposed on the global scope, but `WorkerGlobalScope` (which extends
// `ThrowingEventTarget`) is. Therefore, we get at it in this nasty way.
let __FACADE_EVENT_TARGET__: EventTarget;
if ((globalThis as any).MINIFLARE) {
__FACADE_EVENT_TARGET__ = new (Object.getPrototypeOf(WorkerGlobalScope))();
} else {
__FACADE_EVENT_TARGET__ = new EventTarget();
}
function __facade_isSpecialEvent__(
type: string
): type is "fetch" | "scheduled" {
return type === "fetch" || type === "scheduled";
}
const __facade__originalAddEventListener__ = globalThis.addEventListener;
const __facade__originalRemoveEventListener__ = globalThis.removeEventListener;
const __facade__originalDispatchEvent__ = globalThis.dispatchEvent;
globalThis.addEventListener = function (type, listener, options) {
if (__facade_isSpecialEvent__(type)) {
__FACADE_EVENT_TARGET__.addEventListener(
type,
listener as EventListenerOrEventListenerObject,
options
);
} else {
__facade__originalAddEventListener__(type, listener, options);
}
};
globalThis.removeEventListener = function (type, listener, options) {
if (__facade_isSpecialEvent__(type)) {
__FACADE_EVENT_TARGET__.removeEventListener(
type,
listener as EventListenerOrEventListenerObject,
options
);
} else {
__facade__originalRemoveEventListener__(type, listener, options);
}
};
globalThis.dispatchEvent = function (event) {
if (__facade_isSpecialEvent__(event.type)) {
return __FACADE_EVENT_TARGET__.dispatchEvent(event);
} else {
return __facade__originalDispatchEvent__(event);
}
};
declare global {
var addMiddleware: typeof __facade_register__;
var addMiddlewareInternal: typeof __facade_registerInternal__;
}
globalThis.addMiddleware = __facade_register__;
globalThis.addMiddlewareInternal = __facade_registerInternal__;
const __facade_waitUntil__ = Symbol("__facade_waitUntil__");
const __facade_response__ = Symbol("__facade_response__");
const __facade_dispatched__ = Symbol("__facade_dispatched__");
class __Facade_ExtendableEvent__ extends Event {
[__facade_waitUntil__]: Awaitable<unknown>[] = [];
waitUntil(promise: Awaitable<any>) {
if (!(this instanceof __Facade_ExtendableEvent__)) {
throw new TypeError("Illegal invocation");
}
this[__facade_waitUntil__].push(promise);
}
}
interface FetchEventInit extends EventInit {
request: Request;
passThroughOnException: FetchEvent["passThroughOnException"];
}
class __Facade_FetchEvent__ extends __Facade_ExtendableEvent__ {
#request: Request;
#passThroughOnException: FetchEvent["passThroughOnException"];
[__facade_response__]?: Awaitable<Response>;
[__facade_dispatched__] = false;
constructor(type: "fetch", init: FetchEventInit) {
super(type);
this.#request = init.request;
this.#passThroughOnException = init.passThroughOnException;
}
get request() {
return this.#request;
}
respondWith(response: Awaitable<Response>) {
if (!(this instanceof __Facade_FetchEvent__)) {
throw new TypeError("Illegal invocation");
}
if (this[__facade_response__] !== undefined) {
throw new DOMException(
"FetchEvent.respondWith() has already been called; it can only be called once.",
"InvalidStateError"
);
}
if (this[__facade_dispatched__]) {
throw new DOMException(
"Too late to call FetchEvent.respondWith(). It must be called synchronously in the event handler.",
"InvalidStateError"
);
}
this.stopImmediatePropagation();
this[__facade_response__] = response;
}
passThroughOnException() {
if (!(this instanceof __Facade_FetchEvent__)) {
throw new TypeError("Illegal invocation");
}
// Need to call native method immediately in case uncaught error thrown
this.#passThroughOnException();
}
}
interface ScheduledEventInit extends EventInit {
scheduledTime: number;
cron: string;
noRetry: ScheduledEvent["noRetry"];
}
class __Facade_ScheduledEvent__ extends __Facade_ExtendableEvent__ {
#scheduledTime: number;
#cron: string;
#noRetry: ScheduledEvent["noRetry"];
constructor(type: "scheduled", init: ScheduledEventInit) {
super(type);
this.#scheduledTime = init.scheduledTime;
this.#cron = init.cron;
this.#noRetry = init.noRetry;
}
get scheduledTime() {
return this.#scheduledTime;
}
get cron() {
return this.#cron;
}
noRetry() {
if (!(this instanceof __Facade_ScheduledEvent__)) {
throw new TypeError("Illegal invocation");
}
// Need to call native method immediately in case uncaught error thrown
this.#noRetry();
}
}
__facade__originalAddEventListener__("fetch", (event) => {
const ctx: ExecutionContext = {
waitUntil: event.waitUntil.bind(event),
passThroughOnException: event.passThroughOnException.bind(event),
};
const __facade_sw_dispatch__: Dispatcher = function (type, init) {
if (type === "scheduled") {
const facadeEvent = new __Facade_ScheduledEvent__("scheduled", {
scheduledTime: Date.now(),
cron: init.cron ?? "",
noRetry() {},
});
__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
}
};
const __facade_sw_fetch__: Middleware = function (request, _env, ctx) {
const facadeEvent = new __Facade_FetchEvent__("fetch", {
request,
passThroughOnException: ctx.passThroughOnException,
});
__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
facadeEvent[__facade_dispatched__] = true;
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
const response = facadeEvent[__facade_response__];
if (response === undefined) {
throw new Error("No response!"); // TODO: proper error message
}
return response;
};
event.respondWith(
__facade_invoke__(
event.request as IncomingRequest,
globalThis,
ctx,
__facade_sw_dispatch__,
__facade_sw_fetch__
)
);
});
__facade__originalAddEventListener__("scheduled", (event) => {
const facadeEvent = new __Facade_ScheduledEvent__("scheduled", {
scheduledTime: event.scheduledTime,
cron: event.cron,
noRetry: event.noRetry.bind(event),
});
__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
});

View file

@ -0,0 +1,3 @@
declare module "config:middleware/d1-beta" {
export const D1_IMPORTS: string[];
}

View file

@ -0,0 +1,33 @@
import type { Middleware } from "./common";
interface JsonError {
message?: string;
name?: string;
stack?: string;
cause?: JsonError;
}
function reduceError(e: any): JsonError {
return {
name: e?.name,
message: e?.message ?? String(e),
stack: e?.stack,
cause: e?.cause === undefined ? undefined : reduceError(e.cause),
};
}
// See comment in `bundle.ts` for details on why this is needed
const jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {
try {
return await middlewareCtx.next(request, env);
} catch (e: any) {
const error = reduceError(e);
return Response.json(error, {
status: 500,
headers: { "MF-Experimental-Error-Stack": "true" },
});
}
};
export default jsonError;
export const wrap = undefined;

View file

@ -0,0 +1,4 @@
declare module "config:middleware/multiworker-dev" {
import type { WorkerRegistry } from "../../src/dev-registry";
export const workers: WorkerRegistry;
}

View file

@ -0,0 +1,59 @@
// @ts-nocheck
/// <reference path="middleware-multiworker-dev.d.ts"/>
import { workers } from "config:middleware/multiworker-dev";
import type { WorkerRegistry } from "../../src/dev-registry";
export function wrap(env: Record<string, unknown>) {
const facadeEnv = { ...env };
// For every Worker definition that's available,
// create a fetcher for it on the facade env.
// for const [name, binding] of env
// if Workers[name]
// const details = Workers[name];
for (const [name, details] of Object.entries(workers as WorkerRegistry)) {
if (details) {
facadeEnv[name] = {
async fetch(...reqArgs: Parameters<Fetcher["fetch"]>) {
const reqFromArgs = new Request(...reqArgs);
if (details.headers) {
for (const [key, value] of Object.entries(details.headers)) {
// In remote mode, you need to add a couple of headers
// to make sure it's talking to the 'dev' preview session
// (much like wrangler dev already does via proxy.ts)
reqFromArgs.headers.set(key, value);
}
return (env[name] as Fetcher).fetch(reqFromArgs);
}
const url = new URL(reqFromArgs.url);
if (details.protocol !== undefined) {
url.protocol = details.protocol;
}
if (details.host !== undefined) {
url.host = details.host;
}
if (details.port !== undefined) {
url.port = details.port.toString();
}
const request = new Request(url.toString(), reqFromArgs);
return fetch(request);
},
};
} else {
// This means there's no dev binding available.
// Let's use whatever's available, or put a shim with a message.
facadeEnv[name] = facadeEnv[name] || {
async fetch() {
return new Response(
`You should start up wrangler dev --local on the ${name} worker`,
{ status: 404 }
);
},
};
}
}
return facadeEnv;
}

View file

@ -0,0 +1,40 @@
import type { Middleware } from "./common";
// A middleware has to be a function of type Middleware
const prettyError: Middleware = async (request, env, _ctx, middlewareCtx) => {
try {
const response = await middlewareCtx.next(request, env);
return response;
} catch (e: any) {
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error 🚨</title>
<style>
pre {
margin: 16px auto;
max-width: 600px;
background-color: #eeeeee;
border-radius: 4px;
padding: 16px;
}
</style>
</head>
<body>
<pre>${e.stack}</pre>
</body>
</html>
`;
return new Response(html, {
status: 500,
headers: { "Content-Type": "text/html;charset=utf-8" },
});
}
};
export default prettyError;

View file

@ -0,0 +1,15 @@
import type { Middleware } from "./common";
// A middleware has to be a function of type Middleware
const scheduled: Middleware = async (request, env, _ctx, middlewareCtx) => {
const url = new URL(request.url);
if (url.pathname === "/__scheduled") {
const cron = url.searchParams.get("cron") ?? "";
await middlewareCtx.dispatch("scheduled", { cron });
return new Response("Ran scheduled event");
}
return middlewareCtx.next(request, env);
};
export default scheduled;

View file

@ -0,0 +1,6 @@
declare module "config:middleware/serve-static-assets" {
import type { CacheControl } from "@cloudflare/kv-asset-handler";
export const spaMode: boolean;
export const cacheControl: Partial<CacheControl>;
}

View file

@ -0,0 +1,56 @@
/// <reference path="middleware-serve-static-assets.d.ts"/>
import {
getAssetFromKV,
NotFoundError,
MethodNotAllowedError,
serveSinglePageApp,
} from "@cloudflare/kv-asset-handler";
import type { Options } from "@cloudflare/kv-asset-handler";
import { spaMode, cacheControl } from "config:middleware/serve-static-assets";
import type * as kvAssetHandler from "@cloudflare/kv-asset-handler";
import manifest from "__STATIC_CONTENT_MANIFEST";
const ASSET_MANIFEST = JSON.parse(manifest);
import type { Middleware } from "./common";
const staticAssets: Middleware = async (request, env, _ctx, middlewareCtx) => {
let options: Partial<Options> = {
ASSET_MANIFEST,
ASSET_NAMESPACE: env.__STATIC_CONTENT,
cacheControl: cacheControl,
mapRequestToAsset: spaMode ? serveSinglePageApp : undefined,
};
try {
const page = await (getAssetFromKV as typeof kvAssetHandler.getAssetFromKV)(
{
request,
waitUntil(promise) {
return _ctx.waitUntil(promise);
},
},
options
);
// allow headers to be altered
const response = new Response(page.body, page);
response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("Referrer-Policy", "unsafe-url");
response.headers.set("Feature-Policy", "none");
return response;
} catch (e) {
if (e instanceof NotFoundError || e instanceof MethodNotAllowedError) {
// if a known error is thrown then serve from actual worker
return await middlewareCtx.next(request, env);
}
// otherwise it's a real error, so throw it
throw e;
}
};
export default staticAssets;

View file

@ -0,0 +1,17 @@
/**
* Welcome to Cloudflare Workers! This is your first scheduled worker.
*
* - Run `wrangler dev --local` in your terminal to start a development server
* - Run `curl "http://localhost:8787/cdn-cgi/mf/scheduled"` to trigger the scheduled event
* - Go back to the console to see what your worker has logged
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/wrangler/configuration/#triggers)
* - Run `wrangler publish --name my-worker` to publish your worker
*
* Learn more at https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/
*/
export default {
async scheduled(controller, env, ctx) {
console.log(`Hello World!`);
},
};

View file

@ -0,0 +1,32 @@
/**
* Welcome to Cloudflare Workers! This is your first scheduled worker.
*
* - Run `wrangler dev --local` in your terminal to start a development server
* - Run `curl "http://localhost:8787/cdn-cgi/mf/scheduled"` to trigger the scheduled event
* - Go back to the console to see what your worker has logged
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/wrangler/configuration/#triggers)
* - Run `wrangler deploy --name my-worker` to deploy your worker
*
* Learn more at https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/
*/
export interface Env {
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
// MY_KV_NAMESPACE: KVNamespace;
//
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
// MY_DURABLE_OBJECT: DurableObjectNamespace;
//
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
// MY_BUCKET: R2Bucket;
}
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log(`Hello World!`);
},
};

View file

@ -0,0 +1,15 @@
/**
* Welcome to Cloudflare Workers! This is your first worker.
*
* - Run `npx wrangler dev src/index.js` in your terminal to start a development server
* - Open a browser tab at http://localhost:8787/ to see your worker in action
* - Run `npx wrangler publish src/index.js --name my-worker` to publish your worker
*
* Learn more at https://developers.cloudflare.com/workers/
*/
export default {
async fetch(request, env, ctx) {
return new Response("Hello World!");
},
};

View file

@ -0,0 +1,33 @@
/**
* Welcome to Cloudflare Workers! This is your first worker.
*
* - Run `wrangler dev src/index.ts` in your terminal to start a development server
* - Open a browser tab at http://localhost:8787/ to see your worker in action
* - Run `wrangler deploy src/index.ts --name my-worker` to deploy your worker
*
* Learn more at https://developers.cloudflare.com/workers/
*/
export interface Env {
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
// MY_KV_NAMESPACE: KVNamespace;
//
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
// MY_DURABLE_OBJECT: DurableObjectNamespace;
//
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
// MY_BUCKET: R2Bucket;
//
// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
// MY_SERVICE: Fetcher;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
return new Response("Hello World!");
},
};

View file

@ -0,0 +1,10 @@
export default {
fetch() {
return new Response("Not found", {
status: 404,
headers: {
"Content-Type": "text/html",
},
});
},
};

View file

@ -0,0 +1,31 @@
// @ts-ignore entry point will get replaced
import worker from "__ENTRY_POINT__";
// @ts-ignore entry point will get replaced
export * from "__ENTRY_POINT__";
import { isRoutingRuleMatch } from "./pages-dev-util";
// @ts-ignore routes are injected
const routes = __ROUTES__;
export default <ExportedHandler<{ ASSETS: Fetcher }>>{
fetch(request, env, context) {
const { pathname } = new URL(request.url);
for (const exclude of routes.exclude) {
if (isRoutingRuleMatch(pathname, exclude)) {
return env.ASSETS.fetch(request);
}
}
for (const include of routes.include) {
if (isRoutingRuleMatch(pathname, include)) {
if (worker.fetch === undefined) {
throw new TypeError("Entry point missing `fetch` handler");
}
return worker.fetch(request, env, context);
}
}
return env.ASSETS.fetch(request);
},
};

View file

@ -0,0 +1,55 @@
/**
* @param pathname A pathname string, such as `/foo` or `/foo/bar`
* @param routingRule The routing rule, such as `/foo/*`
* @returns True if pathname matches the routing rule
*
* / -> /
* /* -> /*
* /foo -> /foo
* /foo* -> /foo, /foo-bar, /foo/*
* /foo/* -> /foo, /foo/bar
*/
export function isRoutingRuleMatch(
pathname: string,
routingRule: string
): boolean {
// sanity checks
if (!pathname) {
throw new Error("Pathname is undefined.");
}
if (!routingRule) {
throw new Error("Routing rule is undefined.");
}
const ruleRegExp = transformRoutingRuleToRegExp(routingRule);
return pathname.match(ruleRegExp) !== null;
}
function transformRoutingRuleToRegExp(rule: string): RegExp {
let transformedRule;
if (rule === "/" || rule === "/*") {
transformedRule = rule;
} else if (rule.endsWith("/*")) {
// make `/*` an optional group so we can match both /foo/* and /foo
// /foo/* => /foo(/*)?
transformedRule = `${rule.substring(0, rule.length - 2)}(/*)?`;
} else if (rule.endsWith("/")) {
// make `/` an optional group so we can match both /foo/ and /foo
// /foo/ => /foo(/)?
transformedRule = `${rule.substring(0, rule.length - 1)}(/)?`;
} else if (rule.endsWith("*")) {
transformedRule = rule;
} else {
transformedRule = `${rule}(/)?`;
}
// /foo* => /foo.* => ^/foo.*$
// /*.* => /*\.* => /.*\..* => ^/.*\..*$
transformedRule = `^${transformedRule
.replaceAll(/\./g, "\\.")
.replaceAll(/\*/g, ".*")}$`;
// ^/foo.*$ => /^\/foo.*$/
return new RegExp(transformedRule);
}

View file

@ -0,0 +1,9 @@
// This Worker is used as a default when no Pages Functions are present.
// It proxies the request directly on to the asset server binding.
export default <ExportedHandler<{ ASSETS: Fetcher }>>{
async fetch(request, env, context) {
const response = await env.ASSETS.fetch(request.url, request);
return new Response(response.body, response);
},
};

View file

@ -0,0 +1,190 @@
import { match } from "path-to-regexp";
//note: this explicitly does not include the * character, as pages requires this
const escapeRegex = /[.+?^${}()|[\]\\]/g;
type HTTPMethod =
| "HEAD"
| "OPTIONS"
| "GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE";
/* TODO: Grab these from @cloudflare/workers-types instead */
type Params<P extends string = string> = Record<P, string | string[]>;
type EventContext<Env, P extends string, Data> = {
request: Request;
functionPath: string;
waitUntil: (promise: Promise<unknown>) => void;
passThroughOnException: () => void;
next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
env: Env & { ASSETS: { fetch: typeof fetch } };
params: Params<P>;
data: Data;
};
type EventPluginContext<Env, P extends string, Data, PluginArgs> = {
request: Request;
functionPath: string;
waitUntil: (promise: Promise<unknown>) => void;
passThroughOnException: () => void;
next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
env: Env & { ASSETS: { fetch: typeof fetch } };
params: Params<P>;
data: Data;
pluginArgs: PluginArgs;
};
declare type PagesFunction<
Env = unknown,
P extends string = string,
Data extends Record<string, unknown> = Record<string, unknown>
> = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
declare type PagesPluginFunction<
Env = unknown,
P extends string = string,
Data extends Record<string, unknown> = Record<string, unknown>,
PluginArgs = unknown
> = (
context: EventPluginContext<Env, P, Data, PluginArgs>
) => Response | Promise<Response>;
/* end @cloudflare/workers-types */
type RouteHandler = {
routePath: string;
mountPath: string;
method?: HTTPMethod;
modules: PagesFunction[];
middlewares: PagesFunction[];
};
// inject `routes` via ESBuild
declare const routes: RouteHandler[];
function* executeRequest(request: Request, relativePathname: string) {
// First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
for (const route of [...routes].reverse()) {
if (route.method && route.method !== request.method) {
continue;
}
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
end: false,
});
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
end: false,
});
const matchResult = routeMatcher(relativePathname);
const mountMatchResult = mountMatcher(relativePathname);
if (matchResult && mountMatchResult) {
for (const handler of route.middlewares.flat()) {
yield {
handler,
params: matchResult.params as Params,
path: mountMatchResult.path,
};
}
}
}
// Then look for the first exact route match and execute its "modules"
for (const route of routes) {
if (route.method && route.method !== request.method) {
continue;
}
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
end: true,
});
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
end: false,
});
const matchResult = routeMatcher(relativePathname);
const mountMatchResult = mountMatcher(relativePathname);
if (matchResult && mountMatchResult && route.modules.length) {
for (const handler of route.modules.flat()) {
yield {
handler,
params: matchResult.params as Params,
path: matchResult.path,
};
}
break;
}
}
}
export default function (pluginArgs: unknown) {
const onRequest: PagesPluginFunction = async (workerContext) => {
let { request } = workerContext;
const { env, next } = workerContext;
let { data } = workerContext;
const url = new URL(request.url);
// TODO: Replace this with something actually legible.
const relativePathname = `/${
url.pathname.replace(workerContext.functionPath, "") || ""
}`.replace(/^\/\//, "/");
const handlerIterator = executeRequest(request, relativePathname);
const pluginNext = async (input?: RequestInfo, init?: RequestInit) => {
if (input !== undefined) {
let url = input;
if (typeof input === "string") {
url = new URL(input, request.url).toString();
}
request = new Request(url, init);
}
const result = handlerIterator.next();
// Note we can't use `!result.done` because this doesn't narrow to the correct type
if (result.done === false) {
const { handler, params, path } = result.value;
const context = {
request: new Request(request.clone()),
functionPath: workerContext.functionPath + path,
next: pluginNext,
params,
get data() {
return data;
},
set data(value) {
if (typeof value !== "object" || value === null) {
throw new Error("context.data must be an object");
}
// user has overriden context.data, so we need to merge it with the existing data
data = value;
},
pluginArgs,
env,
waitUntil: workerContext.waitUntil.bind(workerContext),
passThroughOnException:
workerContext.passThroughOnException.bind(workerContext),
};
const response = await handler(context);
return cloneResponse(response);
} else {
return next(request);
}
};
return pluginNext();
};
return onRequest;
}
// This makes a Response mutable
const cloneResponse = (response: Response) =>
// https://fetch.spec.whatwg.org/#null-body-status
new Response(
[101, 204, 205, 304].includes(response.status) ? null : response.body,
response
);

View file

@ -0,0 +1,198 @@
import { match } from "path-to-regexp";
//note: this explicitly does not include the * character, as pages requires this
const escapeRegex = /[.+?^${}()|[\]\\]/g;
type HTTPMethod =
| "HEAD"
| "OPTIONS"
| "GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE";
/* TODO: Grab these from @cloudflare/workers-types instead */
type Params<P extends string = string> = Record<P, string | string[]>;
type EventContext<Env, P extends string, Data> = {
request: Request;
functionPath: string;
waitUntil: (promise: Promise<unknown>) => void;
passThroughOnException: () => void;
next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
env: Env & { ASSETS: { fetch: typeof fetch } };
params: Params<P>;
data: Data;
};
declare type PagesFunction<
Env = unknown,
P extends string = string,
Data extends Record<string, unknown> = Record<string, unknown>
> = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
/* end @cloudflare/workers-types */
type RouteHandler = {
routePath: string;
mountPath: string;
method?: HTTPMethod;
modules: PagesFunction[];
middlewares: PagesFunction[];
};
// inject `routes` via ESBuild
declare const routes: RouteHandler[];
// define `__FALLBACK_SERVICE__` via ESBuild
declare const __FALLBACK_SERVICE__: string;
// expect an ASSETS fetcher binding pointing to the asset-server stage
type FetchEnv = {
[name: string]: { fetch: typeof fetch };
ASSETS: { fetch: typeof fetch };
};
type WorkerContext = {
waitUntil: (promise: Promise<unknown>) => void;
passThroughOnException: () => void;
};
function* executeRequest(request: Request) {
const requestPath = new URL(request.url).pathname;
// First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
for (const route of [...routes].reverse()) {
if (route.method && route.method !== request.method) {
continue;
}
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
end: false,
});
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
end: false,
});
const matchResult = routeMatcher(requestPath);
const mountMatchResult = mountMatcher(requestPath);
if (matchResult && mountMatchResult) {
for (const handler of route.middlewares.flat()) {
yield {
handler,
params: matchResult.params as Params,
path: mountMatchResult.path,
};
}
}
}
// Then look for the first exact route match and execute its "modules"
for (const route of routes) {
if (route.method && route.method !== request.method) {
continue;
}
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
end: true,
});
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
end: false,
});
const matchResult = routeMatcher(requestPath);
const mountMatchResult = mountMatcher(requestPath);
if (matchResult && mountMatchResult && route.modules.length) {
for (const handler of route.modules.flat()) {
yield {
handler,
params: matchResult.params as Params,
path: matchResult.path,
};
}
break;
}
}
}
export default {
async fetch(
originalRequest: Request,
env: FetchEnv,
workerContext: WorkerContext
) {
let request = originalRequest;
const handlerIterator = executeRequest(request);
let data = {}; // arbitrary data the user can set between functions
let isFailOpen = false;
const next = async (input?: RequestInfo, init?: RequestInit) => {
if (input !== undefined) {
let url = input;
if (typeof input === "string") {
url = new URL(input, request.url).toString();
}
request = new Request(url, init);
}
const result = handlerIterator.next();
// Note we can't use `!result.done` because this doesn't narrow to the correct type
if (result.done === false) {
const { handler, params, path } = result.value;
const context = {
request: new Request(request.clone()),
functionPath: path,
next,
params,
get data() {
return data;
},
set data(value) {
if (typeof value !== "object" || value === null) {
throw new Error("context.data must be an object");
}
// user has overriden context.data, so we need to merge it with the existing data
data = value;
},
env,
waitUntil: workerContext.waitUntil.bind(workerContext),
passThroughOnException: () => {
isFailOpen = true;
},
};
const response = await handler(context);
if (!(response instanceof Response)) {
throw new Error("Your Pages function should return a Response");
}
return cloneResponse(response);
} else if (__FALLBACK_SERVICE__) {
// There are no more handlers so finish with the fallback service (`env.ASSETS.fetch` in Pages' case)
const response = await env[__FALLBACK_SERVICE__].fetch(request);
return cloneResponse(response);
} else {
// There was not fallback service so actually make the request to the origin.
const response = await fetch(request);
return cloneResponse(response);
}
};
try {
return await next();
} catch (error) {
if (isFailOpen) {
const response = await env[__FALLBACK_SERVICE__].fetch(request);
return cloneResponse(response);
}
throw error;
}
},
};
// This makes a Response mutable
const cloneResponse = (response: Response) =>
// https://fetch.spec.whatwg.org/#null-body-status
new Response(
[101, 204, 205, 304].includes(response.status) ? null : response.body,
response
);

View file

@ -0,0 +1,11 @@
// @ts-nocheck `@types/node` should NOT be included
Buffer.from("test");
// @ts-expect-error `@types/jest` should NOT be included
test("test");
// `@cloudflare/workers-types` should be included
const _handler: ExportedHandler = {};
new HTMLRewriter();
export {};

View file

@ -0,0 +1,105 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": [
"es2021"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
"jsx": "react" /* Specify what JSX code is generated. */,
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "es2022" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
"types": [
"@cloudflare/workers-types"
] /* Specify type package names to be included without being referenced in a source file. */,
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"resolveJsonModule": true /* Enable importing .json files */,
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
"checkJs": false /* Enable error reporting in type-checked JavaScript files. */,
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
"noEmit": true /* Disable emitting files from a compilation. */,
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
// "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View file

@ -0,0 +1,8 @@
{
"extends": "@cloudflare/workers-tsconfig/tsconfig.json",
"compilerOptions": {
"types": ["@cloudflare/workers-types"]
},
"include": ["**/*.ts"],
"exclude": ["__tests__", "./init-tests/**"]
}