Deno Workshop

2nd week

Software Engineer | Studio XID, Inc.

Microsoft MVP

Deno Korea User Group 4th member

TypeScript Korea User Group Organizer

Marktube (Youtube)

Mark Lee

tsconfig.json ??

function decorator(
  target: Object,
  propertyKey: string | symbol,
  descriptor: PropertyDescriptor,
) {
  console.log("decorator");
}

class Welcome {
  @decorator
  public hello(): void {
    console.log("Welcome to Deno 🦕");
  }
}

function main() {
  new Welcome().hello();
}

if (import.meta.main) {
  main();
}

decorator 가 있는 파일 작성하기

~/Deno 
➜ deno run decorator.ts
Compile file:///Users/mark/Deno/decorator.ts
error: TS1219 [ERROR]: Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
    public hello(): void {
           ~~~~~
    at file:///Users/mark/Deno/decorator.ts:11:12

decorator 가 있는 파일 실행하기

{
  "compilerOptions": {
    "allowJs": false,
    "allowUmdGlobalAccess": false,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "assumeChangesOnlyAffectDirectDependencies": false,
    "checkJs": false,
    "disableSizeLimit": false,
    "generateCpuProfile": "profile.cpuprofile",
    "jsx": "react",
    "jsxFactory": "React.createElement",
    "lib": [],
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitUseStrict": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveConstEnums": false,
    "removeComments": false,
    "resolveJsonModule": true,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "useDefineForClassFields": false
  }
}

default tsconfig

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

custom tsconfig

~/Deno 
➜ deno run -c tsconfig.json decorator.ts 
Compile file:///Users/mark/Deno/decorator.ts
decorator
Welcome to Deno 🦕

나만의 tsconfig 로 사용이 가능

// @deno-types="./foo.d.ts"
import * as foo from "./foo.js";

/// <reference types="./foo.d.ts" />
export const foo = "foo";

js 를 쓰는 경우 type definition 처리 (js 에서만 가능)

custom HTTP header of X-TypeScript-Types

./node_modules/@types/node/index.d.ts

이런건 안해줍니다.

Deno Runtime API

  • Functions

  • Variables

  • Classes

  • Enums

  • Interfaces

  • Type Aliases

  • Namespaces

function callback() {
    console.log('Welcome to Deno 🦕');
}

if (import.meta.main) {
    // function addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions | undefined): void;
    addEventListener('hello', callback);

    // function setInterval(cb: (...args: any[]) => void, delay?: number, ...args: any[]): number;
    setInterval(() => {
        // function dispatchEvent(event: Event): boolean;
        dispatchEvent(new Event('hello'));
    }, 1000);
    // function clearInterval(id?: number): void;

    // function setTimeout(cb: (...args: any[]) => void, delay?: number, ...args: any[]): number;
    setTimeout(() => {
        // function removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined): void;
        removeEventListener('hello', callback);
    }, 2500);
    // function clearTimeout(id?: number): void;
}

Functions : functions1.ts

~/Deno 
➜ deno run functions1.ts 
Compile file:///Users/mark/Deno/functions1.ts
Welcome to Deno 🦕
Welcome to Deno 🦕

deno run functions1.ts

if (import.meta.main) {
  // function btoa(s: string): string;
  console.log(`btoa("hello")`, btoa("hello")); // aGVsbG8=
  console.log(`btoa("Hello World!")`, btoa("Hello World!")); // SGVsbG8gV29ybGQh

  // function atob(s: string): string;
  console.log(`atob("aGVsbG8=")`, atob("aGVsbG8=")); // hello
  console.log(`atob("SGVsbG8gV29ybGQh")`, atob("SGVsbG8gV29ybGQh")); // Hello World!
}

Functions : functions2.ts

~/Deno 
➜ deno run functions2.ts                      
Compile file:///Users/mark/Deno/functions2.ts
btoa("hello") aGVsbG8=
btoa("Hello World!") SGVsbG8gV29ybGQh
atob("aGVsbG8=") hello
atob("SGVsbG8gV29ybGQh") Hello World!

deno run functions2.ts

// function fetch(input: Request | URL | string, init?: RequestInit): Promise<Response>;

if (import.meta.main) {
  const response = await fetch("https://api.github.com/users");
  const users: User[] = await response.json();
  console.log(users.map((user) => user.login));
}

interface User {
  login: string;
  id: number;
  node_id: string;
  avatar_url: string;
  gravatar_id: string;
  url: string;
  html_url: string;
  followers_url: string;
  following_url: string;
  gists_url: string;
  starred_url: string;
  subscriptions_url: string;
  organizations_url: string;
  repos_url: string;
  events_url: string;
  received_events_url: string;
  type: string;
  site_admin: boolean;
}

Functions : fetch.ts

~/Deno 
➜ deno run fetch.ts                           
error: Uncaught PermissionDenied: network access to "https://api.github.com/users", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
    at async fetch ($deno$/web/fetch.ts:265:27)
    at async file:///Users/mark/Deno/fetch.ts:4:22

deno run fetch.ts

~/Deno 
➜ deno run --allow-net=api.github.com fetch.ts
Compile file:///Users/mark/Deno/fetch.ts
[
  "mojombo", "defunkt", "pjhyett",
  "wycats", "ezmobius", "ivey",
  "evanphx", "vanpelt", "wayneeseguin",
  "brynary", "kevinclark", "technoweenie",
  "macournoyer", "takeo", "caged",
  "topfunky", "anotherjesse", "roland",
  "lukas", "fanvsfan", "tomtt",
  "railsjitsu", "nitay", "kevwil",
  "KirinDave", "jamesgolick", "atmos",
  "errfree", "mojodna", "bmizerany"
]

deno run --allow-net=api.github.com fetch.ts

// function queueMicrotask(func: Function): void;

if (import.meta.main) {
  console.log("Before enqueueing the microtask");
  queueMicrotask(() => {
    console.log("The microtask has run.");
  });
  console.log("After enqueueing the microtask");
}

Functions : queueMicrotask.ts

~/Deno 
➜ deno run queueMicrotask.ts 
Compile file:///Users/mark/Deno/queueMicrotask.ts
Before enqueueing the microtask
After enqueueing the microtask
The microtask has run.

deno run queueMicrotask.ts

if (import.meta.main) {
  // function Deno.cwd(): string;
  console.log(Deno.cwd());
  // function Deno.chdir(directory: string): void;
  Deno.chdir("/Users/mark/Desktop");
  console.log(Deno.cwd());

  // function Deno.stat(path: string | URL): Promise<FileInfo>;
  // function Deno.statSync(path: string | URL): FileInfo;
  const fileInfo = await Deno.stat("./foo.txt");
  console.log(fileInfo);

  if (fileInfo.isFile) {
    // function Deno.open(path: string | URL, options?: OpenOptions): Promise<File>;
    // function Deno.openSync(path: string | URL, options?: OpenOptions): File;
    const openedFile = await Deno.open("./foo.txt", { read: true });

    const buffer = new Uint8Array(fileInfo.size);

    // function Deno.read(rid: number, buffer: Uint8Array): Promise<number | null>;
    // function Deno.readSync(rid: number, buffer: Uint8Array): number | null;
    await Deno.read(openedFile.rid, buffer);

    console.log("[read buffer]", new TextDecoder().decode(buffer));

    // function Deno.close(rid: number): void;
    Deno.close(openedFile.rid);

    // function Deno.remove(path: string | URL, options?: RemoveOptions): Promise<void>;
    // function Deno.removeSync(path: string | URL, options?: RemoveOptions): void;
    await Deno.remove("./foo.txt");
  }

  // function Deno.create(path: string | URL): Promise<File>;
  // function Deno.createSync(path: string | URL): File;
  const file = await Deno.create("./foo.txt");

  const text = `Hello, Deno : ${new Date().toISOString()}`;
  const data = new TextEncoder().encode(
    text,
  );

  // function Deno.write(rid: number, data: Uint8Array): Promise<number>;
  // function Deno.writeSync(rid: number, data: Uint8Array): number;
  await Deno.write(file.rid, data);
  console.log("[write text]", text);

  // function Deno.close(rid: number): void;
  Deno.close(file.rid);
}

File System : file.ts

~/Deno 
➜ deno run --allow-read --allow-write file.ts
/Users/mark/Deno
/Users/mark/Desktop
{
  isFile: true,
  isDirectory: false,
  isSymlink: false,
  size: 38,
  mtime: 2020-06-21T13:01:23.938Z,
  atime: 2020-06-21T13:01:27.367Z,
  birthtime: 2020-06-21T13:01:23.937Z,
  dev: 16777222,
  ino: 53370832,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  blocks: 8
}
[read buffer] Hello, Deno : 2020-06-21T13:01:23.937Z
[write text] Hello, Deno : 2020-06-21T13:02:31.480Z

deno run --allow-read --allow-write file.ts

if (import.meta.main) {
  for (let i = 0; i < Deno.args.length; i++) {
    const filename = Deno.args[i];
    const file = await Deno.open(filename);
    await Deno.copy(file, Deno.stdout);
    file.close();
  }
}

Example : cat.ts

~/Deno 
➜ deno run --allow-read cat.ts /Users/mark/Desktop/foo.txt
Hello, Deno : 2020-06-21T13:02:31.480Z%

deno run --allow-read cat.ts /Users/mark/Desktop/foo.txt

async function print() {
  const file = await Deno.open("/Users/mark/Desktop/foo.txt");
  const buffer = new Uint8Array(100);
  await Deno.read(file.rid, buffer);
  const text = new TextDecoder().decode(buffer);
  console.log(text);
  Deno.close(file.rid);
}

if (import.meta.main) {
  const status = await Deno.permissions.query({ name: "read" });
  if (status.state !== "granted") {
    throw new Error("need read permission");
  }
  console.log("read permission exist");
  await print();
  await Deno.permissions.revoke({ name: "read" });
  await print();
}

Example : permission.ts

~/Deno 
➜ deno run permission.ts 
Compile file:///Users/mark/Deno/permission.ts
error: TS2339 [ERROR]: Property 'permissions' does not exist on type 'typeof Deno'.
  const status = await Deno.permissions.query({ name: "read" });
                            ~~~~~~~~~~~
    at file:///Users/mark/Deno/permission.ts:11:29

TS2339 [ERROR]: Property 'permissions' does not exist on type 'typeof Deno'.
  await Deno.permissions.revoke({ name: "read" });
             ~~~~~~~~~~~
    at file:///Users/mark/Deno/permission.ts:17:14

Found 2 errors.

deno run permission.ts

~/Deno 
➜ deno run --unstable permission.ts
Compile file:///Users/mark/Deno/permission.ts
error: Uncaught Error: need read permission
    throw new Error("need read permission");
          ^
    at file:///Users/mark/Deno/permission.ts:13:11

deno run --unstable permission.ts

~/Deno 
➜ deno run --unstable --allow-read permission.ts
read permission exist
Hello, Deno : 2020-06-21T13:02:31.480Z
error: Uncaught PermissionDenied: read access to "/Users/mark/Desktop/foo.txt", run again with the --allow-read flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
    at async Object.open ($deno$/files.ts:37:15)
    at async print (file:///Users/mark/Deno/permission.ts:2:16)
    at async file:///Users/mark/Deno/permission.ts:18:3

deno run --unstable --allow-read permission.ts

if (import.meta.main) {
  const watcher = Deno.watchFs("/Users/mark/Desktop/");
  for await (const event of watcher) {
    console.log(">>>> event", event);
    // { kind: "create", paths: [ "/Users/mark/Desktop/foo.txt" ] }
  }
}

Example : watcher.ts

~/Deno 
➜ deno run --allow-read watcher.ts
Compile file:///Users/mark/Deno/watcher.ts
>>>> event { kind: "remove", paths: [ "/Users/mark/Desktop/foo.txt" ] }
>>>> event { kind: "create", paths: [ "/Users/mark/Desktop/foo.txt" ] }
>>>> event { kind: "modify", paths: [ "/Users/mark/Desktop/foo.txt" ] }
>>>> event { kind: "modify", paths: [ "/Users/mark/Desktop/foo.txt" ] }

deno run --allow-read watcher.ts

if (import.meta.main) {
  const hostname = "0.0.0.0";
  const port = 8080;
  const listener = Deno.listen({ hostname, port });
  console.log(`Listening on ${hostname}:${port}`);
  for await (const conn of listener) {
    console.log(conn);
    Deno.copy(conn, conn);
  }
}

TCP Server : echo_server.ts

~/Deno 
➜ deno run --allow-net echo_server.ts
Compile file:///Users/mark/Deno/echo_server.ts
Listening on 0.0.0.0:8080
ConnImpl {
  rid: 4,
  remoteAddr: { hostname: "127.0.0.1", port: 49878, transport: "tcp" },
  localAddr: { hostname: "127.0.0.1", port: 8080, transport: "tcp" }
}

deno run --allow-net echo_server.ts

~/Deno took 30m 26s 
➜ nc localhost 8080

hello deno
hello deno

nc localhost 8080

import { BufWriter, BufReader } from "https://deno.land/std/io/bufio.ts";
import { writeResponse, readRequest } from "https://deno.land/std/http/_io.ts";

if (import.meta.main) {
  const hostname = "0.0.0.0";
  const port = 8080;
  const listener = Deno.listen({ hostname, port });
  console.log(`Listening on ${hostname}:${port}`);
  for await (const conn of listener) {
    const reader = new BufReader(conn);
    const writer = new BufWriter(conn);

    const request = await readRequest(conn, reader);

    if (request === null) break;

    console.log(request);

    await writeResponse(writer, {
      status: 200,
      body: new TextEncoder().encode(JSON.stringify({ hello: "Deno" })),
    });

    conn.close();
  }
}

HTTP Server : http_server.ts

~/Deno 
➜ deno run --allow-net http_server.ts
Compile file:///Users/mark/Deno/http_server.ts
Listening on 0.0.0.0:8080
ServerRequest {
  done: Promise { <pending> },
  _contentLength: undefined,
  _body: null,
  finalized: false,
  conn: ConnImpl {
    rid: 4,
    remoteAddr: { hostname: "127.0.0.1", port: 58829, transport: "tcp" },
    localAddr: { hostname: "127.0.0.1", port: 8080, transport: "tcp" }
  },
  r: BufReader {
    r: 572,
    w: 572,
    eof: false,
    buf: Uint8Array(4096) [
      71, 69, 84, 32, 47, 32, 72, 84, 84, 80, 47, 49, 46,  49,
      13, 10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104,
      111, 115, 116, 58, 56, 48, 56, 48, 13, 10, 67, 111, 110, 110,
      101, 99, 116, 105, 111, 110, 58, 32, 107, 101, 101, 112, 45,  97,
      108, 105, 118, 101, 13, 10, 67, 97, 99, 104, 101, 45, 67, 111,
      110, 116, 114, 111, 108, 58, 32, 109, 97, 120, 45, 97, 103, 101,
      61, 48, 13, 10, 85, 112, 103, 114, 97, 100, 101, 45, 73, 110,
      115, 101,
      ... 3996 more items
    ],
    rd: ConnImpl {
      rid: 4,
      remoteAddr: { hostname: "127.0.0.1", port: 58829, transport: "tcp" },
      localAddr: { hostname: "127.0.0.1", port: 8080, transport: "tcp" }
    }
  },
  method: "GET",
  url: "/",
  proto: "HTTP/1.1",
  protoMinor: 1,
  protoMajor: 1,
  headers: Headers { host: localhost:8080, connection: keep-alive, cache-control: max-age=0, upgrade-insecure-requests: 1, user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36, accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, sec-fetch-site: none, sec-fetch-mode: navigate, sec-fetch-user: ?1, sec-fetch-dest: document, accept-encoding: gzip, deflate, br, accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7 }
}

deno run --allow-net http_server.ts

import { listenAndServe } from "https://deno.land/std/http/server.ts";

if (import.meta.main) {
  listenAndServe({
    hostname: "localhost",
    port: 8080,
  }, (req) => {
    console.log(req);

    if (req.url === "/") {
      req.respond({
        status: 200,
        body: JSON.stringify({ hello: "Deno" }),
      });

      return;
    }

    req.respond({ status: 404 });
  });
}

Using HTTP module : serve.ts

~/Deno 
➜ deno run --allow-net serve.ts
Compile file:///Users/mark/Deno/serve.ts
ServerRequest {
  done: Promise { <pending> },
  _contentLength: undefined,
  _body: null,
  finalized: false,
  conn: ConnImpl {
    rid: 4,
    remoteAddr: { hostname: "::1", port: 62943, transport: "tcp" },
    localAddr: { hostname: "::1", port: 8080, transport: "tcp" }
  },
  r: BufReader {
    r: 578,
    w: 578,
    eof: false,
    buf: Uint8Array(4096) [
      71, 69, 84, 32, 47, 32, 72, 84, 84, 80, 47, 49, 46,  49,
      13, 10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104,
      111, 115, 116, 58, 56, 48, 56, 48, 13, 10, 67, 111, 110, 110,
      101, 99, 116, 105, 111, 110, 58, 32, 107, 101, 101, 112, 45,  97,
      108, 105, 118, 101, 13, 10, 67, 97, 99, 104, 101, 45, 67, 111,
      110, 116, 114, 111, 108, 58, 32, 109, 97, 120, 45, 97, 103, 101,
      61, 48, 13, 10, 85, 112, 103, 114, 97, 100, 101, 45, 73, 110,
      115, 101,
      ... 3996 more items
    ],
    rd: ConnImpl {
      rid: 4,
      remoteAddr: { hostname: "::1", port: 62943, transport: "tcp" },
      localAddr: { hostname: "::1", port: 8080, transport: "tcp" }
    }
  },
  method: "GET",
  url: "/",
  proto: "HTTP/1.1",
  protoMinor: 1,
  protoMajor: 1,
  headers: Headers { host: localhost:8080, connection: keep-alive, cache-control: max-age=0, upgrade-insecure-requests: 1, user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36, accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, sec-fetch-site: cross-site, sec-fetch-mode: navigate, sec-fetch-user: ?1, sec-fetch-dest: document, accept-encoding: gzip, deflate, br, accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7 },
  w: BufWriter {
    usedBufferBytes: 0,
    err: null,
    writer: ConnImpl {
      rid: 4,
      remoteAddr: { hostname: "::1", port: 62943, transport: "tcp" },
      localAddr: { hostname: "::1", port: 8080, transport: "tcp" }
    },
    buf: Uint8Array(4096) [
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      ... 3996 more items
    ]
  }
}

deno run --allow-net serve.ts

function main(): void {
  const CORSEnabled = serverArgs.cors ? true : false;
  const addr = `0.0.0.0:${serverArgs.port ?? serverArgs.p ?? 4507}`;

  if (serverArgs.h ?? serverArgs.help) {
    console.log(`Deno File Server
    Serves a local directory in HTTP.

  INSTALL:
    deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts

  USAGE:
    file_server [path] [options]

  OPTIONS:
    -h, --help          Prints help information
    -p, --port <PORT>   Set port
    --cors              Enable CORS via the "Access-Control-Allow-Origin" header`);
    Deno.exit();
  }

  listenAndServe(
    addr,
    async (req): Promise<void> => {
      let normalizedUrl = posix.normalize(req.url);
      try {
        normalizedUrl = decodeURIComponent(normalizedUrl);
      } catch (e) {
        if (!(e instanceof URIError)) {
          throw e;
        }
      }
      const fsPath = posix.join(target, normalizedUrl);

      let response: Response | undefined;
      try {
        const fileInfo = await Deno.stat(fsPath);
        if (fileInfo.isDirectory) {
          response = await serveDir(req, fsPath);
        } else {
          response = await serveFile(req, fsPath);
        }
      } catch (e) {
        console.error(e.message);
        response = await serveFallback(req, e);
      } finally {
        if (CORSEnabled) {
          assert(response);
          setCORS(response);
        }
        serverLog(req, response!);
        req.respond(response!);
      }
    }
  );

  console.log(`HTTP server listening on http://${addr}/`);
}

if (import.meta.main) {
  main();
}

File Server : https://deno.land/std/http/file_server.ts

import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const router = new Router();
router
  .get("/", (context) => {
    context.response.body = { hello: "Deno" };
  })
  .get("/book/:id", (context) => {
    if (context.params && context.params.id) {
      context.response.body = context.params.id;
    }
  });

const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());

app.use((ctx) => {
  ctx.response.body = "error";
});

await app.listen({ port: 8080 });

Third Party module : oak.ts

~/Deno 
➜ deno install --allow-read --allow-run --allow-write --allow-net -f --unstable https://deno.land/x/denon@v2.2.0/denon.ts
Download https://deno.land/x/denon@v2.2.0/denon.ts
Download https://deno.land/x/denon@v2.2.0/deps.ts
Download https://deno.land/x/denon@v2.2.0/src/watcher.ts
Download https://deno.land/x/denon@v2.2.0/src/runner.ts
Download https://deno.land/x/denon@v2.2.0/src/daemon.ts
Download https://deno.land/x/denon@v2.2.0/src/cli.ts
Download https://deno.land/x/denon@v2.2.0/src/config.ts
Download https://deno.land/x/denon@v2.2.0/src/args.ts
Download https://deno.land/x/denon@v2.2.0/src/log.ts
Download https://deno.land/std@0.57.0/log/mod.ts
Download https://deno.land/std@0.57.0/log/logger.ts
Download https://deno.land/std@0.57.0/log/levels.ts
Download https://deno.land/std@0.57.0/log/handlers.ts
Download https://deno.land/std@0.57.0/fmt/colors.ts
Download https://deno.land/std@0.57.0/fs/mod.ts
Download https://deno.land/std@0.57.0/encoding/yaml.ts
Download https://deno.land/std@0.57.0/path/mod.ts
Download https://deno.land/std@0.57.0/fs/read_json.ts
Download https://deno.land/std@0.57.0/fs/write_json.ts
Download https://deno.land/std@0.57.0/async/mod.ts
Download https://deno.land/std@0.57.0/permissions/mod.ts
Download https://deno.land/x/omelette/omelette.ts
Download https://deno.land/x/denon@v2.2.0/src/scripts.ts
Download https://deno.land/x/denon@v2.2.0/src/merge.ts
Download https://deno.land/std@0.57.0/fs/exists.ts
Download https://deno.land/std@0.57.0/io/bufio.ts
Download https://deno.land/std@0.57.0/fs/empty_dir.ts
Download https://deno.land/std@0.57.0/fs/ensure_dir.ts
Download https://deno.land/std@0.57.0/fs/ensure_file.ts
Download https://deno.land/std@0.57.0/fs/ensure_link.ts
Download https://deno.land/std@0.57.0/fs/ensure_symlink.ts
Download https://deno.land/std@0.57.0/fs/expand_glob.ts
Download https://deno.land/std@0.57.0/fs/move.ts
Download https://deno.land/std@0.57.0/fs/copy.ts
Download https://deno.land/std@0.57.0/fs/read_file_str.ts
Download https://deno.land/std@0.57.0/fs/write_file_str.ts
Download https://deno.land/std@0.57.0/fs/walk.ts
Download https://deno.land/std@0.57.0/fs/eol.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/parse.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/stringify.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema/mod.ts
Download https://deno.land/std@0.57.0/_util/assert.ts
Download https://deno.land/std@0.57.0/path/_constants.ts
Download https://deno.land/std@0.57.0/path/win32.ts
Download https://deno.land/std@0.57.0/path/posix.ts
Download https://deno.land/std@0.57.0/path/common.ts
Download https://deno.land/std@0.57.0/path/separator.ts
Download https://deno.land/std@0.57.0/path/_interface.ts
Download https://deno.land/std@0.57.0/path/glob.ts
Download https://deno.land/std@0.53.0/node/events.ts
Download https://deno.land/std@0.53.0/path/mod.ts
Download https://deno.land/std@0.57.0/async/deferred.ts
Download https://deno.land/std@0.57.0/async/delay.ts
Download https://deno.land/std@0.57.0/async/mux_async_iterator.ts
Download https://deno.land/std@0.57.0/fs/_util.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/loader/loader.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/loader/loader_state.ts
Download https://deno.land/std@0.57.0/io/util.ts
Download https://deno.land/std@0.57.0/path/_util.ts
Download https://deno.land/std@0.53.0/path/win32.ts
Download https://deno.land/std@0.53.0/path/posix.ts
Download https://deno.land/std@0.53.0/path/common.ts
Download https://deno.land/std@0.53.0/path/separator.ts
Download https://deno.land/std@0.53.0/path/interface.ts
Download https://deno.land/std@0.53.0/path/glob.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema/core.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema/default.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema/failsafe.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/schema/json.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/dumper/dumper.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/dumper/dumper_state.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/utils.ts
Download https://deno.land/std@0.53.0/node/util.ts
Download https://deno.land/std@0.53.0/testing/asserts.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/error.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/mark.ts
Download https://deno.land/std@0.57.0/path/_globrex.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/state.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/mod.ts
Download https://deno.land/std@0.53.0/path/_globrex.ts
Download https://deno.land/std@0.53.0/path/_constants.ts
Download https://deno.land/std@0.53.0/path/_util.ts
Download https://deno.land/std@0.53.0/fmt/colors.ts
Download https://deno.land/std@0.53.0/testing/diff.ts
Download https://deno.land/std@0.53.0/node/_util/_util_callbackify.ts
Download https://deno.land/std@0.53.0/node/_utils.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/binary.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/bool.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/float.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/int.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/map.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/merge.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/nil.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/omap.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/pairs.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/seq.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/set.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/str.ts
Download https://deno.land/std@0.57.0/encoding/_yaml/type/timestamp.ts
Compile https://deno.land/x/denon@v2.2.0/denon.ts
✅ Successfully installed denon
/Users/mark/.deno/bin/denon

deno install --allow-read --allow-run --allow-write --allow-net -f --unsafe https://deno.land/x/denon@v2.2.0/denon.ts

~/Deno 
➜ denon run --allow-net oak.ts
[denon] v2.2.0
[denon] watching path(s): *.*
[denon] watching extensions: ts,tsx,js,jsx,json
[denon] starting `deno run --allow-net oak.ts`
[denon] watching path(s): *.*
[denon] watching extensions: ts,tsx,js,jsx,json
[denon] restarting due to changes...
[denon] starting `deno run --allow-net oak.ts`
Compile file:///Users/mark/Deno/oak.ts
[denon] watching path(s): *.*
[denon] watching extensions: ts,tsx,js,jsx,json
[denon] restarting due to changes...
[denon] starting `deno run --allow-net oak.ts`
Compile file:///Users/mark/Deno/oak.ts

denon run --allow-net oak.ts