6 Commits

11 changed files with 207 additions and 60 deletions

45
deno/app.ts Normal file
View File

@@ -0,0 +1,45 @@
// app.ts
import { services } from "./services";
import{DenoRequest, Method, ProcessedRoute, Request, Route} from './types'
import {routes} from './routes'
services.logging.log({ foo: 1 });
const processedRoutes: ProcessedRoute[] = routes.map(
(route: Route, idx: number, allRoutes: Route[]) => {
const pattern: URLPattern = new URLPattern({ pathname: route.path });
const method: Method = route.method;
const handler = async (denoRequest: DenoRequest) => {
const req: Request = {
pattern: route.pattern,
path: denoRequest.path,
method: denoRequest.method,
parameters: { one: 1, two: 2 },
denoRequest,
};
return route.handler(req);
};
const retval: ProcessedRoute = { pattern, method, handler };
return retval
},
);
async function handler(req: globalThis.Request): globalThis.Response {
for (const [idx, pr] of processedRoutes.entries()) {
const match = pr.pattern.exec(req.url);
if (match) {
const resp = await pr.handler(req);
return new globalThis.Response(resp.result);
}
}
return new globalThis.Response("not found", { status: 404 });
}
Deno.serve(handler);

View File

@@ -1,4 +1,4 @@
import { Extensible } from "./interfaces";
import { Extensible } from "./interfaces.ts";
export type ContentType = string;

View File

@@ -1,4 +1,7 @@
{
"compilerOptions": {
"lib": ["deno.ns", "dom"]
},
"tasks": {
"dev": "deno run --watch main.ts"
},

12
deno/deno.lock generated
View File

@@ -38,7 +38,8 @@
}
},
"redirects": {
"https://deno.land/x/random_number/mod.ts": "https://deno.land/x/random_number@2.0.0/mod.ts"
"https://deno.land/x/random_number/mod.ts": "https://deno.land/x/random_number@2.0.0/mod.ts",
"https://deno.land/x/sleep/mod.ts": "https://deno.land/x/sleep@v1.3.0/mod.ts"
},
"remote": {
"https://deno.land/std@0.214.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5",
@@ -60,6 +61,7 @@
"https://deno.land/std@0.214.0/fmt/colors.ts": "aeaee795471b56fc62a3cb2e174ed33e91551b535f44677f6320336aabb54fbb",
"https://deno.land/std@0.214.0/io/buf_reader.ts": "c73aad99491ee6db3d6b001fa4a780e9245c67b9296f5bad9c0fa7384e35d47a",
"https://deno.land/std@0.214.0/io/buf_writer.ts": "f82f640c8b3a820f600a8da429ad0537037c7d6a78426bbca2396fb1f75d3ef4",
"https://deno.land/std@0.214.0/io/types.ts": "748bbb3ac96abda03594ef5a0db15ce5450dcc6c0d841c8906f8b10ac8d32c96",
"https://deno.land/std@0.214.0/path/_common/assert_path.ts": "2ca275f36ac1788b2acb60fb2b79cb06027198bc2ba6fb7e163efaedde98c297",
"https://deno.land/std@0.214.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
"https://deno.land/std@0.214.0/path/_common/common.ts": "6157c7ec1f4db2b4a9a187efd6ce76dcaf1e61cfd49f87e40d4ea102818df031",
@@ -161,15 +163,18 @@
"https://deno.land/x/postgres@v0.19.3/query/oid.ts": "21fc714ac212350ba7df496f88ea9e01a4ee0458911d0f2b6a81498e12e7af4c",
"https://deno.land/x/postgres@v0.19.3/query/query.ts": "510f9a27da87ed7b31b5cbcd14bf3028b441ac2ddc368483679d0b86a9d9f213",
"https://deno.land/x/postgres@v0.19.3/query/transaction.ts": "8f4eef68f8e9b4be216199404315e6e08fe1fe98afb2e640bffd077662f79678",
"https://deno.land/x/postgres@v0.19.3/query/types.ts": "540f6f973d493d63f2c0059a09f3368071f57931bba68bea408a635a3e0565d6",
"https://deno.land/x/postgres@v0.19.3/utils/deferred.ts": "5420531adb6c3ea29ca8aac57b9b59bd3e4b9a938a4996bbd0947a858f611080",
"https://deno.land/x/postgres@v0.19.3/utils/utils.ts": "ca47193ea03ff5b585e487a06f106d367e509263a960b787197ce0c03113a738",
"https://deno.land/x/random_number@2.0.0/mod.ts": "83010e4a0192b015ba4491d8bb8c73a458f352ebc613b847ff6349961d1c7827",
"https://deno.land/x/redis@v0.37.1/backoff.ts": "33e4a6e245f8743fbae0ce583993a671a3ac2ecee433a3e7f0bd77b5dd541d84",
"https://deno.land/x/redis@v0.37.1/command.ts": "2d1da4b32495ea852bdff0c2e7fd191a056779a696b9f83fb648c5ebac45cfc3",
"https://deno.land/x/redis@v0.37.1/connection.ts": "cee30a6310298441de17d1028d4ce3fd239dcf05a92294fba7173a922d0596cb",
"https://deno.land/x/redis@v0.37.1/deps/std/async.ts": "5a588aefb041cca49f0e6b7e3c397119693a3e07bea89c54cf7fe4a412e37bbf",
"https://deno.land/x/redis@v0.37.1/deps/std/bytes.ts": "f5b437ebcac77600101a81ef457188516e4944b3c2a931dff5ced3fa0c239b62",
"https://deno.land/x/redis@v0.37.1/deps/std/io.ts": "b7505c5e738384f5f7a021d7bbd78380490c059cc7c83cd8dada1f86ec16e835",
"https://deno.land/x/redis@v0.37.1/errors.ts": "8293f56a70ea8388cb80b6e1caa15d350ed1719529fc06573b01a443d0caad69",
"https://deno.land/x/redis@v0.37.1/events.ts": "704767b1beed2d5acfd5e86bd1ef93befdc8a8f8c8bb4ae1b4485664a8a6a625",
"https://deno.land/x/redis@v0.37.1/executor.ts": "5ac4c1f7bec44d12ebc0f3702bf074bd3ba6c1aae74953582f6358d2948718e7",
"https://deno.land/x/redis@v0.37.1/internal/encoding.ts": "0525f7f444a96b92cd36423abdfe221f8d8de4a018dc5cb6750a428a5fc897c2",
"https://deno.land/x/redis@v0.37.1/internal/symbols.ts": "e36097bab1da1c9fe84a3bb9cb0ed1ec10c3dc7dd0b557769c5c54e15d110dd2",
@@ -179,11 +184,14 @@
"https://deno.land/x/redis@v0.37.1/protocol/deno_streams/mod.ts": "b084bf64d6b795f6c1d0b360d9be221e246a9c033e5d88fd1e82fa14f711d25b",
"https://deno.land/x/redis@v0.37.1/protocol/deno_streams/reply.ts": "639de34541f207f793393a3cd45f9a23ef308f094d9d3d6ce62f84b175d3af47",
"https://deno.land/x/redis@v0.37.1/protocol/shared/command.ts": "e75f6be115ff73bd865e01be4e2a28077a9993b1e0c54ed96b6825bfe997d382",
"https://deno.land/x/redis@v0.37.1/protocol/shared/protocol.ts": "5b9284ee28ec74dfc723c7c7f07dca8d5f9d303414f36689503622dfdde12551",
"https://deno.land/x/redis@v0.37.1/protocol/shared/reply.ts": "3311ff66357bacbd60785cb43b97539c341d8a7d963bc5e80cb864ac81909ea5",
"https://deno.land/x/redis@v0.37.1/protocol/shared/types.ts": "c6bf2b9eafd69e358a972823d94b8b478c00bac195b87b33b7437de2a9bb7fb4",
"https://deno.land/x/redis@v0.37.1/pubsub.ts": "a36892455b0a4a50af169332a165b0985cc90d84486087f036e507e3137b2afb",
"https://deno.land/x/redis@v0.37.1/redis.ts": "4904772596c8a82d7112092e7edea45243eae38809b2f2ea8db61a4207fe246b",
"https://deno.land/x/redis@v0.37.1/stream.ts": "d43076815d046eb8428fcd2799544a9fd07b3480099f5fc67d2ba12fdc73725f"
"https://deno.land/x/redis@v0.37.1/stream.ts": "d43076815d046eb8428fcd2799544a9fd07b3480099f5fc67d2ba12fdc73725f",
"https://deno.land/x/sleep@v1.3.0/mod.ts": "e9955ecd3228a000e29d46726cd6ab14b65cf83904e9b365f3a8d64ec61c1af3",
"https://deno.land/x/sleep@v1.3.0/sleep.ts": "b6abaca093b094b0c2bba94f287b19a60946a8d15764d168f83fcf555f5bb59e"
},
"workspace": {
"dependencies": [

0
deno/extensible.ts Normal file
View File

View File

@@ -1 +1,3 @@
export interface Extensible {}
type Brand<K, T> = K & { readonly __brand: T };
export type Extensible = Brand<"Extensible", {}>;

44
deno/logging.ts Normal file
View File

@@ -0,0 +1,44 @@
// internal-logging.ts
// FIXME: Move this to somewhere more appropriate
type AtLeastOne<T> = [T, ...T[]];
type MessageSource = "logging" | "diagnostic" | "user";
type Message = {
// FIXME: number probably isn't what we want here
timestamp: number;
source: MessageSource;
text: AtLeastOne<string>;
};
const m1: Message = { timestamp: 123, source: "logging", text: ["foo"] };
const m2: Message = {
timestamp: 321,
source: "diagnostic",
text: ["ok", "whatever"],
};
type FilterArgument = {
limit?: number;
before?: number;
after?: number;
// FIXME: add offsets to use instead of or in addition to before/after
match?: (string | RegExp)[];
};
const log = (_message: Message) => {
// WRITEME
};
const getLogs = (filter: FilterArgument) => {
// WRITEME
};
// FIXME: there's scope for more specialized functions although they
// probably should be defined in terms of the basic ones here.
export { getLogs, log };

View File

@@ -1,18 +1,16 @@
type Method = "get" | "post" | "put" | "patch" | "delete";
/// <reference lib="dom" />
import { sleep } from "https://deno.land/x/sleep/mod.ts";
import { HttpCode, httpCodes } from "./http-codes.ts";
import { ContentType, contentTypes } from "./content-types";
import { services } from "./services";
import {DenoRequest, Handler, Response, Method, UserRequest, Request, Route, ProcessedRoute } from './types.ts'
type Response = {
code: HttpCode;
contentType: ContentType;
result: string;
};
type Handler = (req: Request) => Response;
const phandler: Handler = (req: Request) => {
const phandler: Handler = async (req: Request) => {
const code = httpCodes.success.OK;
return {
code,
@@ -21,17 +19,54 @@ const phandler: Handler = (req: Request) => {
};
};
type Route = {
path: string;
methods: Method[];
handler: Handler;
// FIXME: Obviously put this somewhere else
const okText = (out: string) => {
const code = httpCodes.success.OK;
return {
code,
result: out,
contentType: contentTypes.text.plain,
};
};
const routes: Route[] = [
{
path: "/slow",
methods: ["get"],
handler: async (_req) => {
console.log("starting slow request") ;
await sleep(10);
console.log("finishing slow request"); return okText("that was slow");
},
},
{
path: "/list",
methods: ["get"],
handler: async (_req) => {
const code = httpCodes.success.OK;
const lr = (rr: Route[]) => {
const ret = rr.map((r: Route) => {
return r.path;
});
return ret;
};
const listing = lr(routes).join(", ");
return {
code,
result: listing + "\n",
contentType: contentTypes.text.plain,
};
},
},
{
path: "/ok",
methods: ["get"],
handler: (req) => {
handler: async (_req) => {
const code = httpCodes.success.OK;
const rn = services.random.randomNumber();
@@ -45,7 +80,7 @@ const routes: Route[] = [
{
path: "/alsook",
methods: ["get"],
handler: (req) => {
handler: async (_req) => {
const code = httpCodes.success.OK;
return {
code,
@@ -56,46 +91,11 @@ const routes: Route[] = [
},
];
type DenoRequest = globalThis.Request;
type UserRequest = {};
type Request = {
pattern: string;
path: string;
method: Method;
parameters: object;
denoRequest: globalThis.Request;
};
type ProcessedRoute = { pattern: URLPattern; method: Method; handler: Handler };
const processedRoutes: ProcessedRoute[] = routes.map(
(route: Route, idx: number, allRoutes: Route[]) => {
const pattern: URLPattern = new URLPattern({ pathname: route.path });
const method: Method = route.method;
const handler = (denoRequest: DenoRequest) => {
const req: Request = {
pattern: route.pattern,
path: denoRequest.path,
method: denoRequest.method,
parameters: { one: 1, two: 2 },
denoRequest,
};
return route.handler(req);
};
return { pattern, method, handler };
},
);
function handler(req: globalThis.Request): globalThis.Response {
for (const [idx, pr] of processedRoutes.entries()) {
const match = pr.pattern.exec(req.url);
if (match) {
const resp = pr.handler(req);
return new globalThis.Response(resp.result);
}
}
return new globalThis.Response("not found", { status: 404 });
}
Deno.serve(handler);
export {routes}

View File

@@ -6,6 +6,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR"
deno run --allow-net --unstable-sloppy-imports --watch routes.ts
deno run --allow-net --unstable-sloppy-imports --watch app.ts

View File

@@ -1,10 +1,9 @@
// services.ts
import { randomNumber } from "https://deno.land/x/random_number/mod.ts";
// import {Client} from './deps'
import { config } from "./config.ts";
import { getLogs, log } from "./logging";
//const database = Client({
@@ -12,12 +11,18 @@ import { config } from "./config.ts";
const database = {};
const logging = {
log,
getLogs,
};
const random = {
randomNumber,
};
const services = {
database,
logging,
random,
};

40
deno/types.ts Normal file
View File

@@ -0,0 +1,40 @@
// types.ts
// FIXME: split this up into types used by app developers and types internal
// to the framework.
import { HttpCode, httpCodes } from "./http-codes.ts";
import { ContentType, contentTypes } from "./content-types";
export type Method = "get" | "post" | "put" | "patch" | "delete";
export type DenoRequest = globalThis.Request;
export type UserRequest = {};
export type Request = {
pattern: string;
path: string;
method: Method;
parameters: object;
denoRequest: globalThis.Request;
};
export type ProcessedRoute = { pattern: URLPattern; method: Method; handler: Handler };
export type Response = {
code: HttpCode;
contentType: ContentType;
result: string;
};
export type Handler = (req: Request) => Promise<Response>;
export type Route = {
path: string;
methods: Method[];
handler: Handler;
interruptable?: boolean
};