Skip to content

Commit

Permalink
fix: use vendor for deps
Browse files Browse the repository at this point in the history
  • Loading branch information
fbritoferreira committed Mar 18, 2024
1 parent 7ce9e2c commit a51e17a
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/jsr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:
- uses: actions/checkout@v4

- name: Publish package
run: npx jsr publish
run: npx jsr publish --allow-slow-types --import-map=./vendor/import_map.json
5 changes: 1 addition & 4 deletions deno.json → jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@
"./src/parser.test.ts": "./src/parser.test.ts",
"./src/parser.ts": "./src/parser.ts",
"./src/types.ts": "./src/types.ts",
"./src/deps.ts": "./src/deps.ts",
"./jsr.json": "./jsr.json",
"./.vscode/settings.json": "./.vscode/settings.json"
},
"imports": {
"@std/assert": "jsr:@std/[email protected]",
"mock_fetch/": "https://deno.land/x/[email protected]/",
"zod/": "https://deno.land/x/[email protected]/"
"@std/assert": "jsr:@std/[email protected]"
}
}
1 change: 0 additions & 1 deletion src/deps.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PlaylistItem } from "./types.ts";
import { M3U8Parser } from "./parser.ts";
import { assertEquals } from "@std/assert";
import * as mf from "mock_fetch/mod.ts";
import * as mf from "../vendor/deno.land/x/mock_fetch@0.3.0/mod.ts";

const sharedPlaylist = `#EXTM3U
#EXTINF:-1 tvg-id="ABC" group-title="News" tvg-logo="http://example.com/logo.png", News Channel
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "./deps.ts";
import { z } from "npm:zod";

export interface PlaylistHeader {
attrs: {
Expand Down
172 changes: 172 additions & 0 deletions vendor/crux.land/api/get/2KNRVU.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2021 denosaurs. All rights reserved. MIT license.

/**
* A deno deploy compatible request handler which can be either sync or async
* and gets passed the `Request`, it then eventually returns a `Response`
*/
export type RequestHandler = (req: Request) => Response | Promise<Response>;

/**
* A handler type for anytime the `MatchHandler` or `other` parameter handler
* fails
*/
export type ErrorHandler = (
req: Request,
err: unknown,
) => Response | Promise<Response>;

/**
* A handler type for anytime a method is received that is not defined
*/
export type UnknownMethodHandler = (
req: Request,
knownMethods: string[],
) => Response | Promise<Response>;

/**
* A handler type for a router path match which gets passed the matched values
*/
export type MatchHandler = (req: Request, match: Record<string, string>) => Response | Promise<Response>;

/**
* A record of route paths and `MatchHandler`s which are called when a match is
* found along with it's values.
*
* The route paths follow the path-to-regexp format with the addition of being able
* to prefix a route with a method name and the `@` sign. For example a route only
* accepting `GET` requests would look like: `GET@/`.
*/
export type Routes = Record<string, MatchHandler>;

/**
* The default other handler for the router
*/
export function defaultOtherHandler(_req: Request): Response {
return new Response(null, {
status: 404,
});
}

/**
* The default error handler for the router
*/
export function defaultErrorHandler(_req: Request, err: unknown): Response {
console.error(err);

return new Response(null, {
status: 500,
});
}

/**
* The default unknown method handler for the router
*/
export function defaultUnknownMethodHandler(
_req: Request,
knownMethods: string[],
): Response {
return new Response(null, {
status: 405,
headers: {
Accept: knownMethods.join(", "),
},
});
}

export const METHODS = [
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"OPTIONS",
"PATCH",
] as const;

const methodRegex = new RegExp(`(?<=^(?:${METHODS.join("|")}))@`);

/**
* A simple and tiny router for deno deploy
*
* ```
* import { listenAndServe } from "https://deno.land/std/http/server.ts";
* import { router } from "https://crux.land/[email protected]";
*
* await listenAndServe(
* ":8080",
* router({
* "/": (_req) => new Response("Hello world!", { status: 200 }),
* }),
* );
* ```
*
* @param routes A record of all routes and their corresponding handler functions
* @param other An optional parameter which contains a handler for anything that
* doesn't match the `routes` parameter
* @param error An optional parameter which contains a handler for any time it
* fails to run the default request handling code
* @param unknownMethod An optional parameter which contains a handler for any time a method
* that is not defined is used
* @returns A deno deploy compatible request handler
*/
export function router(
routes: Routes,
other: RequestHandler = defaultOtherHandler,
error: ErrorHandler = defaultErrorHandler,
unknownMethod: UnknownMethodHandler = defaultUnknownMethodHandler,
): RequestHandler {
return async (req) => {
try {
// route > method > handler
const internalRoutes: Record<string, Record<string, MatchHandler>> = {};

for (const [route, handler] of Object.entries(routes)) {
const [methodOrPath, path] = route.split(methodRegex);

if (path) {
internalRoutes[path] ??= {};
internalRoutes[path][methodOrPath] = handler;
} else {
internalRoutes[methodOrPath] ??= {};
internalRoutes[methodOrPath]["any"] = handler;
}
}

for (const [path, methods] of Object.entries(internalRoutes)) {
const pattern = new URLPattern({
pathname: path,
});
const res = pattern.exec(req.url);

if (res !== null) {
for (const [method, handler] of Object.entries(methods)) {
if (req.method === method) {
const groups: Record<string, string> = {};
for (const [key, value] of Object.entries(res.pathname.groups)) {
if (value !== undefined) {
groups[key] = value;
}
}
return await handler(req, groups);
}
}
if (methods["any"]) {
const groups: Record<string, string> = {};
for (const [key, value] of Object.entries(res.pathname.groups)) {
if (value !== undefined) {
groups[key] = value;
}
}
return await methods["any"](req, groups);
} else {
return await unknownMethod(req, Object.keys(methods));
}
}
}

return await other(req);
} catch (err) {
return error(req, err);
}
};
}
148 changes: 148 additions & 0 deletions vendor/deno.land/x/[email protected]/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { MatchHandler, router, Routes } from "../../../crux.land/api/get/2KNRVU.ts";

export type { MatchHandler };

class UnhandledRouteError extends Error {
routes: Routes;
request: Request;
constructor(init: { request: Request; routes: Routes }) {
const { request, routes } = init;

const method = request.method;
const reqPath = new URL(request.url).pathname;
const routesNumber = Object.entries(routes).length;
const routePlural = routesNumber === 1
? "route has a handler"
: "routes have handlers";

// deno-fmt-ignore
super(`${method} ${reqPath} (${routesNumber} ${routePlural})`);

this.name = this.constructor.name;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}

this.routes = routes;
this.request = request;
}
}

export interface MockFetch {
fetch: typeof globalThis.fetch;
mock: (route: string, handler: MatchHandler) => void;
remove: (route: string) => void;
reset: () => void;
}

/**
* Get a set of functions that do not share any state with the globals.
*
* The returned object can be destructured.
*
* ```
* const { fetch, mock, remove, reset } = sandbox()
* ```
*/
export function sandbox(): MockFetch {
const routeStore = new Map<string, MatchHandler>();

async function fetch(
input: string | Request | URL,
init?: RequestInit,
): Promise<Response> {
// Request constructor won't take a URL, so we need to normalize it first.
if (input instanceof URL) input = input.toString();
const req = new Request(input, init);

const routes = Object.fromEntries(routeStore.entries());

// The router needs to be constructed every time because the routes map is
// very likely to change between fetches.
return await router(
routes,
// If an unhandled route is fetched, throw an error.
(request) => {
throw new UnhandledRouteError({ request, routes });
},
// Errors thrown by a handler, including the unknown route handler, will
// return a 500 Internal Server Error. That's the right behaviour in most
// cases, but we actually *want* that to throw.
(_, error) => {
throw error;
},
)(req);
}

function mock(route: string, handler: MatchHandler) {
routeStore.set(route, handler);
}

function remove(route: string) {
routeStore.delete(route);
}

function reset() {
routeStore.clear();
}

return {
reset,
mock,
remove,
fetch,
};
}

const globalMockFetch = sandbox();

/** This is the function that replaces `fetch` when you call `install()`. */
export const mockedFetch = globalMockFetch.fetch;

/**
* Mock a new route, or override an existing handler.
*
* The route uses URLPattern syntax, with the additional extension of
* (optional) method routing by prefixing with the method,
* eg. `"POST@/user/:id"`.
*
* The handler function may be asynchronous.
*
* ```
* mock("GET@/users/:id", async (_req, params) => {
* const id = parseInt(params["id"]);
* const data = await magicallyGetMyUserData(id);
* return new Response(JSON.stringify(data));
* })
* ```
*/
export const mock = globalMockFetch.mock;

/** Remove an existing route handler. */
export const remove = globalMockFetch.remove;

/** Remove all existing route handlers. */
export const reset = globalMockFetch.reset;

// Store the original fetch so it can be restored later
const originalFetch = globalThis.fetch;

// The functions below are `const` for consistency.

/**
* Replace `globalThis.fetch` with `mockedFetch` (or another function that
* matches the `fetch` signature)
*
* To restore the original `globalThis.fetch`, call `uninstall()`.
*/
export const install = (replacement?: typeof fetch) => {
globalThis.fetch = replacement ?? mockedFetch;
};

/**
* Restore `globalThis.fetch` to what it was before this library was imported.
*/
export const uninstall = () => {
globalThis.fetch = originalFetch;
reset();
};
7 changes: 7 additions & 0 deletions vendor/import_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"imports": {
"https://crux.land/[email protected]": "./crux.land/api/get/2KNRVU.ts",
"https://crux.land/": "./crux.land/",
"https://deno.land/": "./deno.land/"
}
}

0 comments on commit a51e17a

Please sign in to comment.