This repository has been archived by the owner on Jun 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 627
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for v2 registry suggestions (#1980)
Closes: #1974
- Loading branch information
Showing
9 changed files
with
997 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,13 @@ | ||
/* Copyright 2020 the Deno authors. All rights reserved. MIT license. */ | ||
|
||
import { reportAnalytics } from "./analytics.ts"; | ||
import { handleRegistryRequest } from "./registry.ts"; | ||
import { handleConfigRequest } from "./registry_config.ts"; | ||
import { handleApiRequest } from "./suggestions.ts"; | ||
import { handleVSCRequest } from "./vscode.ts"; | ||
import { reportAnalytics } from "./analytics.ts"; | ||
import { ConnInfo } from "https://deno.land/[email protected]/http/server.ts"; | ||
|
||
import type { ConnInfo } from "https://deno.land/[email protected]/http/server.ts"; | ||
import { accepts } from "https://deno.land/x/[email protected]/negotiation.ts"; | ||
|
||
const REMOTE_URL = "https://deno-website2.now.sh"; | ||
|
||
|
@@ -36,37 +40,50 @@ export function withLog( | |
}; | ||
} | ||
|
||
export async function handleRequest(request: Request): Promise<Response> { | ||
const accept = request.headers.get("accept"); | ||
const isHtml = accept && accept.indexOf("html") >= 0; | ||
export function handleRequest(request: Request): Promise<Response> { | ||
const isHtml = request.headers.has("accept") && accepts(request, "text/html"); | ||
|
||
const url = new URL(request.url); | ||
|
||
if (url.pathname === "/v1") { | ||
return Response.redirect("https://deno.land/posts/v1", 301); | ||
return Promise.resolve( | ||
Response.redirect("https://deno.land/posts/v1", 301), | ||
); | ||
} | ||
|
||
if (url.pathname === "/posts") { | ||
return Response.redirect("https://deno.com/blog", 307); | ||
return Promise.resolve(Response.redirect("https://deno.com/blog", 307)); | ||
} | ||
|
||
if (url.pathname.startsWith("/posts/")) { | ||
return Response.redirect( | ||
return Promise.resolve(Response.redirect( | ||
`https://deno.com/blog/${url.pathname.substring("/posts/".length)}`, | ||
307, | ||
); | ||
)); | ||
} | ||
|
||
if (url.pathname.startsWith("/typedoc")) { | ||
return Response.redirect("https://doc.deno.land/builtin/stable", 301); | ||
return Promise.resolve( | ||
Response.redirect("https://doc.deno.land/deno/stable", 301), | ||
); | ||
} | ||
|
||
if (url.pathname.startsWith("/_vsc")) { | ||
return handleVSCRequest(url); | ||
} | ||
|
||
if (url.pathname.startsWith("/_api/")) { | ||
return handleApiRequest(url); | ||
} | ||
|
||
if (["/install.sh", "/install.ps1"].includes(url.pathname)) { | ||
return Response.redirect(`https://deno.land/x/install${url.pathname}`, 307); | ||
return Promise.resolve( | ||
Response.redirect(`https://deno.land/x/install${url.pathname}`, 307), | ||
); | ||
} | ||
|
||
if (url.pathname === "/.well-known/deno-import-intellisense.json") { | ||
return handleConfigRequest(request); | ||
} | ||
|
||
const isRegistryRequest = url.pathname.startsWith("/std") || | ||
|
@@ -76,15 +93,17 @@ export async function handleRequest(request: Request): Promise<Response> { | |
if (isHtml) { | ||
const ln = extractAltLineNumberReference(url.toString()); | ||
if (ln) { | ||
return Response.redirect(ln.rest + "#L" + ln.line, 302); | ||
return Promise.resolve( | ||
Response.redirect(`${ln.rest}#L${ln.line}`, 302), | ||
); | ||
} | ||
} else { | ||
return handleRegistryRequest(url); | ||
} | ||
} | ||
|
||
if (!["HEAD", "GET"].includes(request.method)) { | ||
return new Response(null, { status: 405 }); // Method not allowed. | ||
return Promise.resolve(new Response(null, { status: 405 })); // Method not allowed. | ||
} | ||
|
||
return proxyFile(url, REMOTE_URL, request); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
#!/usr/bin/env -S deno run --allow-read=. --allow-net --allow-env | ||
/* Copyright 2020 the Deno authors. All rights reserved. MIT license. */ | ||
|
||
import { handleRequest, withLog } from "./handler.ts"; | ||
import { listenAndServe } from "https://deno.land/[email protected]/http/server.ts"; | ||
|
||
const handler = withLog(handleRequest); | ||
|
||
console.log("The server is available at http://localhost:8080"); | ||
listenAndServe(":8080", handler); | ||
console.log("The server is available at http://localhost:8081"); | ||
listenAndServe(":8081", handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/* Copyright 2021 the Deno authors. All rights reserved. MIT license. */ | ||
|
||
import { accepts } from "https://deno.land/x/[email protected]/negotiation.ts"; | ||
|
||
interface RegistryDefVariable { | ||
key: string; | ||
documentation?: string; | ||
url: string; | ||
} | ||
|
||
interface RegistryDef { | ||
schema: string; | ||
variables: RegistryDefVariable[]; | ||
} | ||
|
||
interface RegistryConfig { | ||
version: 1 | 2; | ||
registries: RegistryDef[]; | ||
} | ||
|
||
const MAX_AGE_1_DAY = "max-age=86400"; | ||
|
||
/** The _legacy_ v1 configuration file. This will be provided to any client | ||
* which does not indicate it is capable of understanding the v2 registry | ||
* (earlier than Deno 1.17.1) */ | ||
const configV1: RegistryConfig = { | ||
version: 1, | ||
registries: [ | ||
{ | ||
schema: "/x/:module([a-z0-9_]*)@:version?/:path*", | ||
variables: [ | ||
{ | ||
key: "module", | ||
url: "https://api.deno.land/modules?simple=1", | ||
}, | ||
{ | ||
key: "version", | ||
url: "https://deno.land/_vsc1/modules/${module}", | ||
}, | ||
{ | ||
key: "path", | ||
url: "https://deno.land/_vsc1/modules/${module}/v/${{version}}", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/x/:module([a-z0-9_]*)/:path*", | ||
variables: [ | ||
{ | ||
key: "module", | ||
url: "https://api.deno.land/modules?simple=1", | ||
}, | ||
{ | ||
key: "path", | ||
url: "https://deno.land/_vsc1/modules/${module}/v_latest", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/std@:version?/:path*", | ||
variables: [ | ||
{ | ||
key: "version", | ||
url: "https://deno.land/_vsc1/modules/std", | ||
}, | ||
{ | ||
key: "path", | ||
url: "https://deno.land/_vsc1/modules/std/v/${{version}}", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/std/:path*", | ||
variables: [ | ||
{ | ||
key: "path", | ||
url: "https://deno.land/_vsc1/modules/std/v_latest", | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
/** This is the v2 registry configuration which provides documentation | ||
* endpoints and allows incremental completion/search of variables. */ | ||
const configV2: RegistryConfig = { | ||
version: 2, | ||
registries: [ | ||
{ | ||
schema: "/x/:module([a-z0-9_]+)@:version?/:path*", | ||
variables: [ | ||
{ | ||
key: "module", | ||
documentation: "/_api/details/x/${module}", | ||
url: "/_api/x/${module}", | ||
}, | ||
{ | ||
key: "version", | ||
documentation: "/_api/details/x/${module}/${{version}}", | ||
url: "/_api/x/${module}/${{version}}", | ||
}, | ||
{ | ||
key: "path", | ||
documentation: "/_api/details/x/${module}/${{version}}/${path}", | ||
url: "/_api/x/${module}/${{version}}/${path}", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/x/:module([a-z0-9_]*)/:path*", | ||
variables: [ | ||
{ | ||
key: "module", | ||
documentation: "/_api/details/x/${module}", | ||
url: "/_api/x/${module}", | ||
}, | ||
{ | ||
key: "path", | ||
documentation: "/_api/details/x/${module}/_latest/${path}", | ||
url: "/_api/x/${module}/_latest/${path}", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/std@:version?/:path*", | ||
variables: [ | ||
{ | ||
key: "version", | ||
documentation: "/_api/details/std/${{version}}", | ||
url: "/_api/x/std/${{version}}", | ||
}, | ||
{ | ||
key: "path", | ||
documentation: "/_api/details/std/${{version}}/${path}", | ||
url: "/_api/x/std/${{version}}/${path}", | ||
}, | ||
], | ||
}, | ||
{ | ||
schema: "/std/:path*", | ||
variables: [ | ||
{ | ||
key: "path", | ||
documentation: "/_api/details/std/_latest/${path}", | ||
url: "/_api/x/std/_latest/${path}", | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
/** Provide the v1 or v2 registry configuration based on the accepts header | ||
* provided by the client. Deno 1.17.1 and later indicates it accepts a | ||
* configuration of v2. */ | ||
export function handleConfigRequest(request: Request): Promise<Response> { | ||
let body: unknown; | ||
let contentType = "application/json"; | ||
if ( | ||
request.headers.has("accept") && | ||
accepts(request, "application/vnd.deno.reg.v2+json") | ||
) { | ||
contentType = "application/vnd.deno.reg.v2+json"; | ||
body = configV2; | ||
} else { | ||
body = configV1; | ||
} | ||
return Promise.resolve( | ||
new Response(JSON.stringify(body), { | ||
headers: { | ||
"cache-control": MAX_AGE_1_DAY, | ||
"content-type": contentType, | ||
}, | ||
}), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* Copyright 2021-2022 the Deno authors. All rights reserved. MIT license. */ | ||
|
||
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts"; | ||
|
||
import { handleConfigRequest } from "./registry_config.ts"; | ||
|
||
Deno.test({ | ||
name: "handleConfigRequest - v1 version of manifest", | ||
async fn() { | ||
const req = new Request( | ||
"https://deno.land//.well-known/deno-import-intellisense.json", | ||
); | ||
const res = await handleConfigRequest(req); | ||
assertEquals(res.status, 200); | ||
const json = await res.json(); | ||
assertEquals(json.version, 1); | ||
}, | ||
}); | ||
|
||
Deno.test({ | ||
name: "handleConfigRequest - browser", | ||
async fn() { | ||
const req = new Request( | ||
"https://deno.land//.well-known/deno-import-intellisense.json", | ||
{ | ||
headers: { | ||
"accept": | ||
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", | ||
}, | ||
}, | ||
); | ||
const res = await handleConfigRequest(req); | ||
assertEquals(res.status, 200); | ||
const json = await res.json(); | ||
assertEquals(json.version, 2); | ||
}, | ||
}); | ||
|
||
Deno.test({ | ||
name: "handleConfigRequest - v2 version of manifest", | ||
async fn() { | ||
const req = new Request( | ||
"https://deno.land//.well-known/deno-import-intellisense.json", | ||
{ | ||
headers: { | ||
// this is the accept header Deno v 1.17.1 and later sends in the request | ||
accept: | ||
"application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8", | ||
}, | ||
}, | ||
); | ||
const res = await handleConfigRequest(req); | ||
assertEquals(res.status, 200); | ||
const json = await res.json(); | ||
assertEquals(json.version, 2); | ||
}, | ||
}); |
Oops, something went wrong.