Skip to content

Commit

Permalink
fix: updates to support Deno 2
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Oct 7, 2024
1 parent de20780 commit 042034f
Show file tree
Hide file tree
Showing 9 changed files with 23 additions and 110 deletions.
8 changes: 4 additions & 4 deletions application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,29 +643,29 @@ export class Application<AS extends State = Record<string, any>>

/** Add an event listener for a `"close"` event which occurs when the
* application is closed and no longer listening or handling requests. */
addEventListener<S extends AS>(
override addEventListener<S extends AS>(
type: "close",
listener: ApplicationCloseEventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions,
): void;
/** Add an event listener for an `"error"` event which occurs when an
* un-caught error occurs when processing the middleware or during processing
* of the response. */
addEventListener<S extends AS>(
override addEventListener<S extends AS>(
type: "error",
listener: ApplicationErrorEventListenerOrEventListenerObject<S, AS> | null,
options?: boolean | AddEventListenerOptions,
): void;
/** Add an event listener for a `"listen"` event which occurs when the server
* has successfully opened but before any requests start being processed. */
addEventListener(
override addEventListener(
type: "listen",
listener: ApplicationListenEventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions,
): void;
/** Add an event listener for an event. Currently valid event types are
* `"error"` and `"listen"`. */
addEventListener(
override addEventListener(
type: "close" | "error" | "listen",
listener: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions,
Expand Down
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
theme: jekyll-theme-minimal
theme: jekyll-theme-minimal
6 changes: 3 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<meta charset="utf-8">
<meta charset="utf-8" />
<title>Redirecting to https://oakserver.org/</title>
<meta http-equiv="refresh" content="0; URL=https://oakserver.org/">
<link rel="canonical" href="https://oakserver.org/">
<meta http-equiv="refresh" content="0; URL=https://oakserver.org/" />
<link rel="canonical" href="https://oakserver.org/" />
14 changes: 6 additions & 8 deletions examples/static/index.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<!DOCTYPE html>
<html>
<head></head>

<head></head>

<body>
<h1>Hello Static World!</h1>
<img src="deno_logo.png" width="150" />
</body>

</html>
<body>
<h1>Hello Static World!</h1>
<img src="deno_logo.png" width="150" />
</body>
</html>
11 changes: 2 additions & 9 deletions middleware/etag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ import type { Context } from "../context.ts";
import { eTag, type ETagOptions } from "../deps.ts";
import type { Middleware } from "../middleware.ts";
import { BODY_TYPES } from "../utils/consts.ts";
import { isAsyncIterable, isReader } from "../utils/type_guards.ts";

// This is to work around issue introduced in Deno 1.40
// See: https://github.com/denoland/deno/issues/22115
function isFsFile(value: unknown): value is Deno.FsFile {
return !!(value && typeof value === "object" && "stat" in value &&
typeof value.stat === "function");
}
import { isAsyncIterable, isFsFile } from "../utils/type_guards.ts";

/** For a given Context, try to determine the response body entity that an ETag
* can be calculated from. */
Expand All @@ -36,7 +29,7 @@ export function getEntity<S extends State = Record<string, any>>(
if (BODY_TYPES.includes(typeof body)) {
return Promise.resolve(String(body));
}
if (isAsyncIterable(body) || isReader(body)) {
if (isAsyncIterable(body)) {
return Promise.resolve(undefined);
}
if (typeof body === "object" && body !== null) {
Expand Down
7 changes: 3 additions & 4 deletions response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
import { contentType, isRedirectStatus, Status, STATUS_TEXT } from "./deps.ts";
import { DomResponse } from "./http_server_native_request.ts";
import type { Request } from "./request.ts";
import { isAsyncIterable, isHtml, isReader } from "./utils/type_guards.ts";
import { isAsyncIterable, isFsFile, isHtml } from "./utils/type_guards.ts";
import { BODY_TYPES } from "./utils/consts.ts";
import { encodeUrl } from "./utils/encode_url.ts";
import {
readableStreamFromAsyncIterable,
readableStreamFromReader,
Uint8ArrayTransformStream,
} from "./utils/streams.ts";

Expand Down Expand Up @@ -64,8 +63,8 @@ async function convertBodyToBodyInit(
if (BODY_TYPES.includes(typeof body)) {
result = String(body);
type = type ?? (isHtml(result) ? "html" : "text/plain");
} else if (isReader(body)) {
result = readableStreamFromReader(body);
} else if (isFsFile(body)) {
result = body.readable;
} else if (
ArrayBuffer.isView(body) || body instanceof ArrayBuffer ||
body instanceof Blob || body instanceof URLSearchParams
Expand Down
2 changes: 1 addition & 1 deletion send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export async function send(
root,
} = options;
const trailingSlash = path[path.length - 1] === "/";
path = decodeComponent(path.substr(parse(path).root.length));
path = decodeComponent(path.substring(parse(path).root.length));
if (index && trailingSlash) {
path += index;
}
Expand Down
76 changes: 0 additions & 76 deletions utils/streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,8 @@

import { BODY_TYPES } from "./consts.ts";

interface Reader {
read(p: Uint8Array): Promise<number | null>;
}

interface Closer {
close(): void;
}

interface ReadableStreamFromReaderOptions {
/** If the `reader` is also a `Closer`, automatically close the `reader`
* when `EOF` is encountered, or a read error occurs.
*
* Defaults to `true`. */
autoClose?: boolean;

/** The size of chunks to allocate to read, the default is ~16KiB, which is
* the maximum size that Deno operations can currently support. */
chunkSize?: number;

/** The queuing strategy to create the `ReadableStream` with. */
strategy?: { highWaterMark?: number | undefined; size?: undefined };
}

function isCloser(value: unknown): value is Deno.Closer {
return typeof value === "object" && value != null && "close" in value &&
// deno-lint-ignore no-explicit-any
typeof (value as Record<string, any>)["close"] === "function";
}

const DEFAULT_CHUNK_SIZE = 16_640; // 17 Kib

const encoder = new TextEncoder();

/**
* Create a `ReadableStream<Uint8Array>` from a `Reader`.
*
* When the pull algorithm is called on the stream, a chunk from the reader
* will be read. When `null` is returned from the reader, the stream will be
* closed along with the reader (if it is also a `Closer`).
*/
export function readableStreamFromReader(
reader: Reader | (Reader & Closer),
options: ReadableStreamFromReaderOptions = {},
): ReadableStream<Uint8Array> {
const {
autoClose = true,
chunkSize = DEFAULT_CHUNK_SIZE,
strategy,
} = options;

return new ReadableStream({
async pull(controller) {
const chunk = new Uint8Array(chunkSize);
try {
const read = await reader.read(chunk);
if (read === null) {
if (isCloser(reader) && autoClose) {
reader.close();
}
controller.close();
return;
}
controller.enqueue(chunk.subarray(0, read));
} catch (e) {
controller.error(e);
if (isCloser(reader)) {
reader.close();
}
}
},
cancel() {
if (isCloser(reader) && autoClose) {
reader.close();
}
},
}, strategy);
}

/**
* Create a `ReadableStream<Uint8Array>` from an `AsyncIterable`.
*/
Expand Down
7 changes: 3 additions & 4 deletions utils/type_guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ export function isNode(): boolean {
!("Bun" in globalThis) && !("WebSocketPair" in globalThis);
}

/** Guard for `Deno.Reader`. */
export function isReader(value: unknown): value is Deno.Reader {
return typeof value === "object" && value !== null && "read" in value &&
typeof (value as Record<string, unknown>).read === "function";
export function isFsFile(value: unknown): value is Deno.FsFile {
return !!(value && typeof value === "object" && "stat" in value &&
typeof value.stat === "function");
}

export function isRouterContext<
Expand Down

0 comments on commit 042034f

Please sign in to comment.