aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-10-20 04:18:29 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-10-20 04:18:29 -0700
commit58b196c58310a1489b554d16ec2695c7974eaab3 (patch)
treee631543d9b9891e01bd35e5374e987ef600a524d
parent3dc53c3d1327f9e86467d6509cb94faacfd26580 (diff)
downloadbun-58b196c58310a1489b554d16ec2695c7974eaab3.tar.gz
bun-58b196c58310a1489b554d16ec2695c7974eaab3.tar.zst
bun-58b196c58310a1489b554d16ec2695c7974eaab3.zip
rewrote most of the router
-rw-r--r--packages/bun-framework-next/client.development.tsx35
-rw-r--r--packages/bun-framework-next/fallback.development.tsx4
-rw-r--r--packages/bun-framework-next/renderDocument.tsx12
-rw-r--r--packages/bun-framework-next/server.development.tsx1
-rw-r--r--src/api/schema.d.ts2
-rw-r--r--src/api/schema.js11
-rw-r--r--src/api/schema.peechy2
-rw-r--r--src/api/schema.zig6
-rw-r--r--src/bundler.zig9
-rw-r--r--src/fallback.version2
-rw-r--r--src/global.zig6
-rw-r--r--src/http.zig58
m---------src/javascript/jsc/WebKit0
-rw-r--r--src/javascript/jsc/javascript.zig40
-rw-r--r--src/js_lexer.zig13
-rw-r--r--src/query_string_map.zig22
-rw-r--r--src/router.zig2238
-rw-r--r--src/string_immutable.zig39
-rw-r--r--src/test/fixtures.zig554
19 files changed, 1657 insertions, 1397 deletions
diff --git a/packages/bun-framework-next/client.development.tsx b/packages/bun-framework-next/client.development.tsx
index c27f5c660..8d2f2d485 100644
--- a/packages/bun-framework-next/client.development.tsx
+++ b/packages/bun-framework-next/client.development.tsx
@@ -82,38 +82,9 @@ function nextDataFromBunData() {
Object.assign(params, Object.fromEntries(url.searchParams.entries()));
Object.assign(params, Object.fromEntries(paramsMap.entries()));
- const pages = routes.reduce((acc, route) => {
- var name = route.substring(route.indexOf("_next") + "_next/".length);
-
- while (name.startsWith("/")) {
- name = name.substring(1);
- }
-
- if (name.startsWith("pages")) {
- name = name.substring("pages".length);
- }
-
- while (name.startsWith("/")) {
- name = name.substring(1);
- }
-
- if (name.endsWith(".jsx")) {
- name = name.substring(0, name.length - ".jsx".length);
- }
-
- if (name.endsWith(".tsx")) {
- name = name.substring(0, name.length - ".tsx".length);
- }
-
- if (name.endsWith(".ts")) {
- name = name.substring(0, name.length - ".ts".length);
- }
-
- if (name.endsWith(".js")) {
- name = name.substring(0, name.length - ".js".length);
- }
-
- acc["/" + name] = [route];
+ const pages = routes.keys.reduce((acc, routeName, i) => {
+ const routePath = routes.values[i];
+ acc[routeName] = [routePath];
return acc;
}, {});
diff --git a/packages/bun-framework-next/fallback.development.tsx b/packages/bun-framework-next/fallback.development.tsx
index e0663108f..7378df037 100644
--- a/packages/bun-framework-next/fallback.development.tsx
+++ b/packages/bun-framework-next/fallback.development.tsx
@@ -37,7 +37,7 @@ function renderFallback({
reason,
problems,
}: FallbackMessageContainer) {
- const route = router.routes[router.route];
+ const route = router.routes.values[router.route];
if (!document.getElementById("__next")) {
const next = document.createElement("div");
@@ -51,7 +51,7 @@ function renderFallback({
});
globalThis.__NEXT_DATA__.pages["/_app"] = [
- ...globalThis.__NEXT_DATA__.pages["/_app"],
+ ...(globalThis.__NEXT_DATA__.pages["/_app"] || []),
...globalThis["__BUN"].allImportedStyles,
];
diff --git a/packages/bun-framework-next/renderDocument.tsx b/packages/bun-framework-next/renderDocument.tsx
index dfe80c3f7..aa6c85fbf 100644
--- a/packages/bun-framework-next/renderDocument.tsx
+++ b/packages/bun-framework-next/renderDocument.tsx
@@ -373,6 +373,7 @@ export async function render({
DocumentNamespace = null,
buildId,
routePaths = [],
+ routeNames = [],
}: {
buildId: number;
route: any;
@@ -382,6 +383,7 @@ export async function render({
appStylesheets: string[];
pageStylesheets: string[];
routePaths: string[];
+ routeNames: string[];
request: Request;
}): Promise<Response> {
const { default: Component, getStaticProps = null } = PageNamespace || {};
@@ -393,12 +395,10 @@ export async function render({
var asPath = route.pathname;
const pages = {};
- for (let path of routePaths) {
- const filePath = path.substring(
- path.indexOf("_next/pages/") + "_next/pages".length
- );
- const name = filePath.substring(0, filePath.indexOf("."));
- pages[name] = [Bun.origin + path];
+ for (let i = 0; i < routeNames.length; i++) {
+ const filePath = routePaths[i];
+ const name = routeNames[i];
+ pages[name] = [Bun.origin + filePath];
}
if (appStylesheets.length > 0) {
diff --git a/packages/bun-framework-next/server.development.tsx b/packages/bun-framework-next/server.development.tsx
index cd8487f35..29c40c6be 100644
--- a/packages/bun-framework-next/server.development.tsx
+++ b/packages/bun-framework-next/server.development.tsx
@@ -63,6 +63,7 @@ addEventListener("fetch", async (event: FetchEvent) => {
AppNamespace: appRoute,
buildId,
routePaths: Bun.getRouteFiles(),
+ routeNames: Bun.getRouteNames(),
request: event.request,
})
);
diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts
index 36b9a6fcd..89f9af7b9 100644
--- a/src/api/schema.d.ts
+++ b/src/api/schema.d.ts
@@ -300,7 +300,7 @@ type uint32 = number;
}
export interface Router {
- routes: string[];
+ routes: StringMap;
route: int32;
params: StringMap;
}
diff --git a/src/api/schema.js b/src/api/schema.js
index 029a3378d..f733ce2cb 100644
--- a/src/api/schema.js
+++ b/src/api/schema.js
@@ -416,9 +416,7 @@ function encodeProblems(message, bb) {
function decodeRouter(bb) {
var result = {};
- var length = bb.readVarUint();
- var values = result["routes"] = Array(length);
- for (var i = 0; i < length; i++) values[i] = bb.readString();
+ result["routes"] = decodeStringMap(bb);
result["route"] = bb.readInt32();
result["params"] = decodeStringMap(bb);
return result;
@@ -428,12 +426,7 @@ function encodeRouter(message, bb) {
var value = message["routes"];
if (value != null) {
- var values = value, n = values.length;
- bb.writeVarUint(n);
- for (var i = 0; i < n; i++) {
- value = values[i];
- bb.writeString(value);
- }
+ encodeStringMap(value, bb);
} else {
throw new Error("Missing required field \"routes\"");
}
diff --git a/src/api/schema.peechy b/src/api/schema.peechy
index 15bb279f4..2cc78504a 100644
--- a/src/api/schema.peechy
+++ b/src/api/schema.peechy
@@ -84,7 +84,7 @@ struct Problems {
}
struct Router {
- string[] routes;
+ StringMap routes;
int32 route;
StringMap params;
}
diff --git a/src/api/schema.zig b/src/api/schema.zig
index e34131dd2..7b60bf346 100644
--- a/src/api/schema.zig
+++ b/src/api/schema.zig
@@ -647,7 +647,7 @@ pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
pub const Router = struct {
/// routes
-routes: []const []const u8,
+routes: StringMap,
/// route
route: i32 = 0,
@@ -659,14 +659,14 @@ params: StringMap,
pub fn decode(reader: anytype) anyerror!Router {
var this = std.mem.zeroes(Router);
- this.routes = try reader.readArray([]const u8);
+ this.routes = try reader.readValue(StringMap);
this.route = try reader.readValue(i32);
this.params = try reader.readValue(StringMap);
return this;
}
pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
- try writer.writeArray([]const u8, this.routes);
+ try writer.writeValue(this.routes);
try writer.writeInt(this.route);
try writer.writeValue(this.params);
}
diff --git a/src/bundler.zig b/src/bundler.zig
index 87f37d220..3504b04c0 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -384,11 +384,10 @@ pub const Bundler = struct {
this.options.routes.routes_enabled = true;
this.router = try Router.init(this.fs, this.allocator, this.options.routes);
try this.router.?.loadRoutes(
+ this.log,
dir_info,
Resolver,
&this.resolver,
- std.math.maxInt(u16),
- true,
);
this.router.?.routes.client_framework_enabled = this.options.isFrontendFrameworkEnabled();
return;
@@ -401,7 +400,7 @@ pub const Bundler = struct {
this.options.routes.dir = dir_info.abs_path;
this.router = try Router.init(this.fs, this.allocator, this.options.routes);
- try this.router.?.loadRoutes(dir_info, Resolver, &this.resolver, std.math.maxInt(u16), true);
+ try this.router.?.loadRoutes(this.log, dir_info, Resolver, &this.resolver);
this.router.?.routes.client_framework_enabled = this.options.isFrontendFrameworkEnabled();
return;
}
@@ -866,7 +865,7 @@ pub const Bundler = struct {
defer this.bundler.resetStore();
Analytics.Features.filesystem_router = true;
- const entry_points = try router.getEntryPoints(allocator);
+ const entry_points = try router.getEntryPoints();
for (entry_points) |entry_point| {
const source_dir = bundler.fs.top_level_dir;
const resolved = try bundler.linker.resolver.resolve(source_dir, entry_point, .entry_point);
@@ -2743,7 +2742,7 @@ pub const Bundler = struct {
var load_from_routes = false;
if (bundler.options.routes.routes_enabled and bundler.options.entry_points.len == 0) {
if (bundler.router) |router| {
- bundler.options.entry_points = try router.getEntryPoints(allocator);
+ bundler.options.entry_points = try router.getEntryPoints();
skip_normalize = true;
load_from_routes = true;
}
diff --git a/src/fallback.version b/src/fallback.version
index 75f3323de..b8908f649 100644
--- a/src/fallback.version
+++ b/src/fallback.version
@@ -1 +1 @@
-a5559a0075104616 \ No newline at end of file
+7aa588534c09f455 \ No newline at end of file
diff --git a/src/global.zig b/src/global.zig
index b55a64a8d..d1ac73a32 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -87,10 +87,12 @@ pub const Output = struct {
return enable_ansi_colors and !isWindows;
}
+ var _source_for_test: if (isTest) Output.Source else void = undefined;
pub fn initTest() void {
var in = std.io.getStdErr();
- var src = Output.Source.init(in, in);
- Output.Source.set(&src);
+ var out = std.io.getStdOut();
+ _source_for_test = Output.Source.init(out, in);
+ Output.Source.set(&_source_for_test);
}
pub fn enableBuffering() void {
enable_buffering = true;
diff --git a/src/http.zig b/src/http.zig
index 4ceccfaa4..b7c28d0b8 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -82,7 +82,6 @@ pub const RequestContext = struct {
matched_route: ?Router.Match = null,
full_url: [:0]const u8 = "",
- match_file_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined,
res_headers_count: usize = 0,
pub fn getFullURL(this: *RequestContext) [:0]const u8 {
@@ -134,11 +133,13 @@ pub const RequestContext = struct {
args: anytype,
) !void {
var route_index: i32 = -1;
- const routes: []const string = if (bundler_.router != null) brk: {
+ const routes: Api.StringMap = if (bundler_.router != null) brk: {
const router = &bundler_.router.?;
- var list = try router.getEntryPointsWithBuffer(allocator, false);
- break :brk list.entry_points;
- } else &([_]string{});
+ break :brk Api.StringMap{
+ .keys = router.getNames() catch unreachable,
+ .values = router.getPublicPaths() catch unreachable,
+ };
+ } else std.mem.zeroes(Api.StringMap);
var preload: string = "";
var params: Api.StringMap = std.mem.zeroes(Api.StringMap);
@@ -184,35 +185,12 @@ pub const RequestContext = struct {
if (this.matched_route) |match| {
if (match.params.len > 0) {
- var all = try allocator.alloc(string, match.params.len * 2);
- var keys = all[0..match.params.len];
- var values = all[match.params.len..];
- var slice = match.params.slice();
-
- var _keys: []Router.TinyPtr = slice.items(.key);
- var _values: []Router.TinyPtr = slice.items(.value);
-
- for (_keys) |key, i| {
- keys[i] = key.str(match.name);
- values[i] = _values[i].str(match.pathnameWithoutLeadingSlash());
- }
-
- params.keys = keys;
- params.values = values;
+ params.keys = match.params.items(.name);
+ params.values = match.params.items(.value);
}
- for (routes) |route, i| {
- var comparator = route;
- if (comparator[0] == '/') comparator = comparator[1..];
-
- if (this.bundler.router.?.config.asset_prefix_path.len > 0) {
- comparator = comparator[this.bundler.router.?.config.asset_prefix_path.len..];
- }
-
- if (strings.endsWith(match.file_path, comparator)) {
- route_index = @truncate(i32, @intCast(i64, i));
- break;
- }
+ if (this.bundler.router.?.routeIndexByHash(match.hash)) |ind| {
+ route_index = @intCast(i32, ind);
}
}
@@ -220,7 +198,10 @@ pub const RequestContext = struct {
defer allocator.destroy(fallback_container);
fallback_container.* = Api.FallbackMessageContainer{
.message = try std.fmt.allocPrint(allocator, fmt, args),
- .router = if (routes.len > 0) Api.Router{ .route = route_index, .params = params, .routes = routes } else null,
+ .router = if (routes.keys.len > 0)
+ Api.Router{ .route = route_index, .params = params, .routes = routes }
+ else
+ null,
.reason = step,
.cwd = this.bundler.fs.top_level_dir,
.problems = Api.Problems{
@@ -1259,7 +1240,7 @@ pub const RequestContext = struct {
}
var one: [1]*JavaScriptHandler = undefined;
- pub fn enqueue(ctx: *RequestContext, server: *Server, filepath_buf: []u8, params: *Router.Param.List) !void {
+ pub fn enqueue(ctx: *RequestContext, server: *Server, params: *Router.Param.List) !void {
if (JavaScriptHandler.javascript_disabled) {
try ctx.renderFallback(
ctx.allocator,
@@ -1286,15 +1267,6 @@ pub const RequestContext = struct {
clone.ctx.conn = &clone.conn;
clone.ctx.matched_route.?.params = &clone.params;
- clone.ctx.matched_route.?.file_path = filepath_buf[0..ctx.matched_route.?.file_path.len];
-
- // this copy may be unnecessary, i'm not 100% sure where when
- std.mem.copy(u8, &clone.ctx.match_file_path_buf, filepath_buf[0..ctx.matched_route.?.file_path.len]);
-
- // Ensure the matched route name pointers to the filepath buffer instead of whatever it was before
- if (strings.indexOf(clone.ctx.matched_route.?.file_path, ctx.matched_route.?.name)) |i| {
- clone.ctx.matched_route.?.name = Router.Match.nameWithBasename(clone.ctx.matched_route.?.file_path, ctx.bundler.router.?.config.dir);
- }
if (!has_loaded_channel) {
var handler_thread = try server.allocator.create(HandlerThread);
diff --git a/src/javascript/jsc/WebKit b/src/javascript/jsc/WebKit
-Subproject 487a7b31de9fa54dab1611799db26907c14dc5a
+Subproject 58007798f366ae8bb7487ca5256a6db860c6da4
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 7749b5182..b9f34d2d3 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -359,16 +359,35 @@ pub const Bun = struct {
) js.JSValueRef {
if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx);
- const router = &(VirtualMachine.vm.bundler.router orelse unreachable);
- const list = router.getEntryPointsWithBuffer(VirtualMachine.vm.allocator, false) catch unreachable;
- VirtualMachine.vm.flush_list.append(list.buffer) catch {};
- defer VirtualMachine.vm.allocator.free(list.entry_points);
+ const router = &VirtualMachine.vm.bundler.router.?;
+ const list = router.getPublicPaths() catch unreachable;
- for (routes_list_strings[0..std.math.min(list.entry_points.len, routes_list_strings.len)]) |_, i| {
- routes_list_strings[i] = ZigString.init(list.entry_points[i]);
+ for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| {
+ routes_list_strings[i] = ZigString.init(list[i]);
}
- const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.entry_points.len).asRef();
+ const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.len).asRef();
+ return ref;
+ }
+
+ pub fn getRouteNames(
+ this: void,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx);
+
+ const router = &VirtualMachine.vm.bundler.router.?;
+ const list = router.getNames() catch unreachable;
+
+ for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| {
+ routes_list_strings[i] = ZigString.init(list[i]);
+ }
+
+ const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.len).asRef();
return ref;
}
@@ -478,6 +497,13 @@ pub const Bun = struct {
.@"return" = "string[]",
},
},
+ .getRouteNames = .{
+ .rfn = Bun.getRouteNames,
+ .ts = d.ts{
+ .name = "getRouteNames",
+ .@"return" = "string[]",
+ },
+ },
.readFile = .{
.rfn = Bun.readFileAsString,
.ts = d.ts{
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
index fe4bf6a12..2196e40dd 100644
--- a/src/js_lexer.zig
+++ b/src/js_lexer.zig
@@ -2709,6 +2709,19 @@ pub fn isIdentifier(text: string) bool {
return true;
}
+test "isIdentifier" {
+ const expect = std.testing.expect;
+ try expect(!isIdentifierContinue(':'));
+ try expect(!isIdentifier("javascript:"));
+
+ try expect(isIdentifier("javascript"));
+
+ try expect(!isIdentifier(":2"));
+ try expect(!isIdentifier("2:"));
+ try expect(isIdentifier("$"));
+ try expect(!isIdentifier("$:"));
+}
+
pub fn isIdentifierUTF16(text: JavascriptString) bool {
const n = text.len;
if (n == 0) {
diff --git a/src/query_string_map.zig b/src/query_string_map.zig
index a5bc2f6bd..4c149dce1 100644
--- a/src/query_string_map.zig
+++ b/src/query_string_map.zig
@@ -798,6 +798,19 @@ pub const CombinedScanner = struct {
}
};
+fn stringPointerFromStrings(parent: string, in: string) Api.StringPointer {
+ if (in.len == 0 or parent.len == 0) return Api.StringPointer{};
+
+ const end = @ptrToInt(parent.ptr) + parent.len;
+ const in_end = @ptrToInt(in.ptr) + in.len;
+ if (in_end < end) return Api.StringPointer{};
+
+ return Api.StringPointer{
+ .offset = @truncate(u32, @maximum(@ptrToInt(in.ptr), @ptrToInt(parent.ptr)) - @ptrToInt(parent.ptr)),
+ .length = @truncate(u32, in.len),
+ };
+}
+
pub const PathnameScanner = struct {
params: *ParamsList,
pathname: string,
@@ -827,11 +840,14 @@ pub const PathnameScanner = struct {
defer this.i += 1;
const param = this.params.get(this.i);
+
return Scanner.Result{
- .name = param.key.toStringPointer(),
+ // TODO: fix this technical debt
+ .name = stringPointerFromStrings(this.routename, param.name),
.name_needs_decoding = false,
- .value = param.value.toStringPointer(),
- .value_needs_decoding = std.mem.indexOfScalar(u8, param.value.str(this.pathname), '%') != null,
+ // TODO: fix this technical debt
+ .value = stringPointerFromStrings(this.pathname, param.value),
+ .value_needs_decoding = std.mem.indexOfScalar(u8, param.value, '%') != null,
};
}
};
diff --git a/src/router.zig b/src/router.zig
index 90ff23b30..86d5e2481 100644
--- a/src/router.zig
+++ b/src/router.zig
@@ -17,11 +17,18 @@ const URLPath = @import("./http/url_path.zig");
const PathnameScanner = @import("./query_string_map.zig").PathnameScanner;
const CodepointIterator = @import("./string_immutable.zig").CodepointIterator;
-const index_route_hash = @truncate(u32, std.hash.Wyhash.hash(0, "index"));
+const index_route_hash = @truncate(u32, std.hash.Wyhash.hash(0, "$$/index-route$$-!(@*@#&*%-901823098123"));
const arbitrary_max_route = 4096;
+pub const Param = struct {
+ name: string,
+ value: string,
+
+ pub const List = std.MultiArrayList(Param);
+};
+
dir: StoredFileDescriptorType = 0,
-routes: RouteMap,
+routes: Routes,
loaded_routes: bool = false,
allocator: *std.mem.Allocator,
fs: *Fs.FileSystem,
@@ -33,11 +40,10 @@ pub fn init(
config: Options.RouteConfig,
) !Router {
return Router{
- .routes = RouteMap{
- .routes = RouteGroup.Root.initEmpty(),
- .index = null,
- .allocator = allocator,
+ .routes = Routes{
.config = config,
+ .allocator = allocator,
+ .static = std.StringHashMap(*Route).init(allocator),
},
.fs = fs,
.allocator = allocator,
@@ -45,248 +51,167 @@ pub fn init(
};
}
-pub const EntryPointList = struct {
- entry_points: []const string,
- buffer: []u8,
-};
-pub fn getEntryPointsWithBuffer(this: *const Router, allocator: *std.mem.Allocator, comptime absolute: bool) !EntryPointList {
- var i: u16 = 0;
- const route_count: u16 = @truncate(u16, this.routes.routes.len);
-
- var count: usize = 0;
- var str_len: usize = 0;
-
- while (i < route_count) : (i += 1) {
- const children = this.routes.routes.items(.children)[i];
- count += @intCast(
- usize,
- @boolToInt(children.len == 0),
- );
- if (children.len == 0) {
- const entry = this.routes.routes.items(.entry)[i];
- str_len += entry.base().len + entry.dir.len;
- }
- }
+pub fn getEntryPoints(this: *const Router) ![]const string {
+ return this.routes.list.items(.filepath);
+}
- var buffer = try allocator.alloc(u8, str_len + count);
- var remain = buffer;
- var entry_points = try allocator.alloc(string, count);
-
- i = 0;
- var entry_point_i: usize = 0;
- while (i < route_count) : (i += 1) {
- const children = this.routes.routes.items(.children)[i];
- if (children.len == 0) {
- const entry = this.routes.routes.items(.entry)[i];
- if (comptime absolute) {
- var parts = [_]string{ entry.dir, entry.base() };
- entry_points[entry_point_i] = this.fs.absBuf(&parts, remain);
- } else {
- var parts = [_]string{ "/", this.config.asset_prefix_path, this.fs.relativeTo(entry.dir), entry.base() };
- entry_points[entry_point_i] = this.fs.joinBuf(&parts, remain);
- }
+pub fn getPublicPaths(this: *const Router) ![]const string {
+ return this.routes.list.items(.public_path);
+}
- remain = remain[entry_points[entry_point_i].len..];
- entry_point_i += 1;
- }
+pub fn routeIndexByHash(this: *const Router, hash: u32) ?usize {
+ if (hash == index_route_hash) {
+ return this.routes.index_id;
}
- return EntryPointList{ .entry_points = entry_points, .buffer = buffer };
+ return std.mem.indexOfScalar(u32, this.routes.list.items(.hash), hash);
}
-pub fn getEntryPoints(this: *const Router, allocator: *std.mem.Allocator) ![]const string {
- const list = try getEntryPointsWithBuffer(this, allocator, true);
- return list.entry_points;
+pub fn getNames(this: *const Router) ![]const string {
+ return this.routes.list.items(.name);
}
const banned_dirs = [_]string{
"node_modules",
};
-const RouteEntry = struct {
+const RouteIndex = struct {
route: *Route,
+ name: string,
+ match_name: string,
+ filepath: string,
+ public_path: string,
hash: u32,
- pub const List = std.MultiArrayList(RouteEntry);
-
- pub fn indexInList(hashes: []u32, hash: u32) ?u32 {
- for (hashes) |hash_, i| {
- if (hash_ == hash) return @truncate(u32, i);
- }
-
- return null;
- }
+ pub const List = std.MultiArrayList(RouteIndex);
};
-pub const RouteGroup = struct {
- /// **Static child routes**
- /// Each key's pointer starts at the offset of the pattern string
- ///
- /// When no more dynamic paramters exist for the route, it will live in this hash table.
- ///
- /// index routes live in the parent's `RouteGroup` and do not have `"index"` in the key
- ///
- /// `"edit"` -> `"pages/posts/[id]/edit.js"`
- /// `"posts/all"` -> `"pages/posts/all.js"`
- /// `"posts"` -> `"pages/posts/index.js"` or `"pages/posts.js"`
-
- static: std.StringArrayHashMapUnmanaged(*Route) = std.StringArrayHashMapUnmanaged(*Route){},
- child: ?*RouteGroup = null,
-
-/// **Dynamic Route**
-///
-/// When it's the final pattern in the route and there was no index route, this route will match. Only matches when there is still text for a single segment.
-///
-/// `posts/[id]` -> `"pages/posts/[id].js"`
-
- dynamic: ?*Route = null,
-
- /// **Catch all route**
-///
-///
-///
-/// `posts/[id]` -> `"pages/posts/[id].js"`
- catch_all: ?*Route = null,
- catch_all_is_optional: bool = false,
-
- offset: u32 = 0,
-
- pub const zero = RouteGroup{};
-
- pub fn isEmpty(this: *const RouteGroup) bool {
- this.dy
- return this.static.count() == 0 and this.child == null and this.index == null and this.dynamic == null and this.catch_all == null;
- }
+pub const Routes = struct {
+ list: RouteIndex.List = RouteIndex.List{},
+ dynamic: []*Route = &[_]*Route{},
+ dynamic_names: []string = &[_]string{},
+ dynamic_match_names: []string = &[_]string{},
- pub fn init() RouteGroup {
- return RouteGroup{
- .index = null,
- .static = std.StringArrayHashMapUnmanaged(*Route){},
- };
- }
-
- pub fn insert(this: *RouteGroup, allocator: *std.mem.Allocator, routes: []*Route, offset: u32) u32 {
- if (comptime isDebug) {
- std.debug.assert(offset > 0);
- std.debug.assert(this.offset == 0 or this.offset == offset);
- }
+ /// completely static children of indefinite depth
+ /// `"blog/posts"`
+ /// `"dashboard"`
+ /// `"profiles"`
+ /// this is a fast path?
+ static: std.StringHashMap(*Route),
- this.offset = offset;
-
- var i: usize = 0;
- while (i < routes.len) {
- var j: usize = i + 1;
- defer i = j;
- }
- }
-
- pub const Root = struct {
- all: []*Route = &[_]*Route{},
+ /// Corresponds to "index.js" on the filesystem
+ index: ?*Route = null,
+ index_id: ?usize = 0,
- /// completely static children of indefinite depth
- /// `"blog/posts"`
- /// `"dashboard"`
- /// `"profiles"`
- /// this is a fast path?
- static: std.StringHashMap(*Route),
+ allocator: *std.mem.Allocator,
+ config: Options.RouteConfig,
- /// The root can only have one of these
- /// These routes have at least one parameter somewhere
- children: ?RouteGroup = null,
+ // This is passed here and propagated through Match
+ // We put this here to avoid loading the FrameworkConfig for the client, on the server.
+ client_framework_enabled: bool = false,
- /// Corresponds to "index.js" on the filesystem
- index: ?*Route = null,
+ pub fn matchPage(this: *Routes, routes_dir: string, url_path: URLPath, params: *Param.List) ?Match {
+ // Trim trailing slash
+ var path = url_path.path;
+ var redirect = false;
- pub fn initEmpty() Root {
- return Root{
- .static = std.StringHashMap(*Route).init(default_allocator),
- .children = std.StringArrayHashMap(RouteGroup).init(default_allocator),
- };
+ // Normalize trailing slash
+ // "/foo/bar/index/" => "/foo/bar/index"
+ if (path.len > 0 and path[path.len - 1] == '/') {
+ path = path[0 .. path.len - 1];
+ redirect = true;
}
- pub fn insert(this: *Root, allocator: *std.mem.Allocator, log: *Logger.Log, children: []*Route) void {
- var i: u32 = 0;
- var end = @intCast(u32, children.len);
- var j: u32 = 0;
- while (i < children.len) {
- var route = children[i];
+ // Normal case: "/foo/bar/index" => "/foo/bar"
+ // Pathological: "/foo/bar/index/index/index/index/index/index" => "/foo/bar"
+ // Extremely pathological: "/index/index/index/index/index/index/index" => "index"
+ while (strings.endsWith(path, "/index")) {
+ path = path[0 .. path.len - "/index".len];
+ redirect = true;
+ }
- if (comptime isDebug) {
- std.debug.assert(route.param_count > 0);
- }
+ if (strings.eqlComptime(path, "index")) {
+ path = "";
+ redirect = true;
+ }
- const first_pattern = Pattern.init(route.name, 0) catch unreachable;
-
- // Since routes are sorted by [ appearing last
- // and we make all static routes fit into a separate hash table first
- // we can assume that if the pattern is the last one, it's a dynamic route of some kind
- if (first_pattern.isEnd(route.name)) {
- switch (first_pattern.value) {
- .static => unreachable, // thats a bug
- .dynamic => {
- if (this.dynamic != null) {
- log.addErrorFmt(null, Logger.Loc.Empty, allocator,
- \\Multiple dynamic routes can't be on the root route. Rename either:
- \\
- \\ {s}
- \\ {s}
- \\
- , .{
- route.abs_path.str(), this.dynamic.?.abs_path.str(),
- });
- }
+ // one final time, trim trailing slash
+ while (path.len > 0 and path[path.len - 1] == '/') {
+ path = path[0 .. path.len - 1];
+ redirect = true;
+ }
- this.dynamic = route;
- },
- .optional_catch_all, .catch_all => {
- if (this.fallback != null) {
- log.addErrorFmt(null, Logger.Loc.Empty, allocator,
- \\Multiple catch-all routes can't be on the root route. Rename either:
- \\
- \\ {s}
- \\ {s}
- \\
- , .{
- route.abs_path.str(), this.fallback.?.abs_path.str(),
- });
- }
+ if (strings.eqlComptime(path, ".")) {
+ path = "";
+ redirect = false;
+ }
- this.fallback = route;
- },
- }
+ if (path.len == 0) {
+ if (this.index) |index| {
+ return Match{
+ .params = params,
+ .name = index.name,
+ .path = index.abs_path.slice(),
+ .pathname = url_path.pathname,
+ .basename = index.entry.base(),
+ .hash = index_route_hash,
+ .file_path = index.abs_path.slice(),
+ .query_string = url_path.query_string,
+ .client_framework_enabled = this.client_framework_enabled,
+ };
+ }
- return;
- }
+ return null;
+ }
- j = i + 1;
- defer i = j;
+ const MatchContextType = struct {
+ params: Param.List,
+ };
+ var matcher = MatchContextType{ .params = params.* };
+ defer params.* = matcher.params;
+
+ if (this.match(this.allocator, path, *MatchContextType, &matcher)) |route| {
+ return Match{
+ .params = params,
+ .name = route.name,
+ .path = route.abs_path.slice(),
+ .pathname = url_path.pathname,
+ .basename = route.entry.base(),
+ .hash = route.full_hash,
+ .file_path = route.abs_path.slice(),
+ .query_string = url_path.query_string,
+ .client_framework_enabled = this.client_framework_enabled,
+ };
+ }
- if (j >= children.len) {
- var entry = this.children.getOrPut(hashed_string.str()) catch unreachable;
- if (!entry.found_existing) {
- entry.value_ptr.* = RouteGroup.init(allocator);
- }
+ return null;
+ }
- _ = entry.value_ptr.insert(routes[@maximum(@as(usize, i), 1) - 1 ..], 0);
- return;
- }
+ fn matchDynamic(this: *Routes, allocator: *std.mem.Allocator, path: string, comptime MatchContext: type, ctx: MatchContext) ?*Route {
+ // its cleaned, so now we search the big list of strings
+ var i: usize = 0;
+ while (i < this.dynamic_names.len) : (i += 1) {
+ const name = this.dynamic_match_names[i];
+ const case_sensitive_name_without_leading_slash = this.dynamic_names[i][1..];
+ var offset: u32 = 0;
+ if (Pattern.match(path, name, case_sensitive_name_without_leading_slash, allocator, *@TypeOf(ctx.params), &ctx.params, true)) {
+ return this.dynamic[i];
+ }
+ }
- var second_route = children[j];
- var second_pattern = Pattern.init(second_route.name, 0) catch unreachable;
- var prev_pattern = second_pattern;
- while (j < children.len and first_pattern.eql(second_pattern)) : (j += 1) {
- prev_pattern = second_pattern;
- second_route = children[j];
- }
+ return null;
+ }
- if (this.children == null) {
- this.children = RouteGroup.init(allocator);
- }
+ fn match(this: *Routes, allocator: *std.mem.Allocator, pathname_: string, comptime MatchContext: type, ctx: MatchContext) ?*Route {
+ var pathname = std.mem.trimLeft(u8, pathname_, "/");
- this.children.?.insert(routes[i..j], first_pattern.len);
- }
+ if (pathname.len == 0) {
+ return this.index;
}
- };
+
+ return this.static.get(pathname) orelse
+ this.matchDynamic(allocator, pathname, MatchContext, ctx);
+ }
};
const RouteLoader = struct {
@@ -294,11 +219,10 @@ const RouteLoader = struct {
fs: *FileSystem,
config: Options.RouteConfig,
- list: RouteEntry.List,
+ dedupe_dynamic: std.AutoArrayHashMap(u32, string),
log: *Logger.Log,
index: ?*Route = null,
static_list: std.StringHashMap(*Route),
-
all_routes: std.ArrayListUnmanaged(*Route),
pub fn appendRoute(this: *RouteLoader, route: Route) void {
@@ -306,6 +230,7 @@ const RouteLoader = struct {
if (route.full_hash == index_route_hash) {
var new_route = this.allocator.create(Route) catch unreachable;
this.index = new_route;
+ new_route.* = route;
this.all_routes.append(this.allocator, new_route) catch unreachable;
return;
}
@@ -320,7 +245,7 @@ const RouteLoader = struct {
&source,
Logger.Loc.Empty,
this.allocator,
- "Route {s} is already defined by {s}",
+ "Route \"{s}\" is already defined by {s}",
.{ route.name, entry.value_ptr.*.abs_path.slice() },
) catch unreachable;
return;
@@ -333,26 +258,16 @@ const RouteLoader = struct {
return;
}
- // dynamic-ish
{
- // This becomes a dead pointer at the end
- var slice = this.list.slice();
-
- const hashes = slice.items(.hash);
- if (std.mem.indexOfScalar(u32, hashes, route.full_hash)) |i| {
- const routes = slice.items(.route);
-
- if (comptime isDebug) {
- std.debug.assert(strings.eql(routes[i].name, route.name));
- }
-
+ const entry = this.dedupe_dynamic.getOrPutValue(route.full_hash, route.abs_path.slice()) catch unreachable;
+ if (entry.found_existing) {
const source = Logger.Source.initEmptyFile(route.abs_path.slice());
this.log.addErrorFmt(
&source,
Logger.Loc.Empty,
this.allocator,
- "Route {s} is already defined by {s}",
- .{ route.name, routes[i].abs_path.slice() },
+ "Route \"{s}\" is already defined by {s}",
+ .{ route.name, entry.value_ptr.* },
) catch unreachable;
return;
}
@@ -361,52 +276,86 @@ const RouteLoader = struct {
{
var new_route = this.allocator.create(Route) catch unreachable;
new_route.* = route;
-
- this.list.append(
- this.allocator,
- .{
- .hash = route.full_hash,
- .route = new_route,
- },
- ) catch unreachable;
this.all_routes.append(this.allocator, new_route) catch unreachable;
}
}
- pub fn loadAll(allocator: *std.mem.Allocator, config: Options.RouteConfig, log: *Logger.Log, comptime ResolverType: type, resolver: *ResolverType, root_dir_info: *const DirInfo) RouteGroup.Root {
+ pub fn loadAll(allocator: *std.mem.Allocator, config: Options.RouteConfig, log: *Logger.Log, comptime ResolverType: type, resolver: *ResolverType, root_dir_info: *const DirInfo) Routes {
var this = RouteLoader{
.allocator = allocator,
.log = log,
.fs = resolver.fs,
.config = config,
- .list = .{},
.static_list = std.StringHashMap(*Route).init(allocator),
+ .dedupe_dynamic = std.AutoArrayHashMap(u32, string).init(allocator),
.all_routes = .{},
};
+ defer this.dedupe_dynamic.deinit();
this.load(ResolverType, resolver, root_dir_info);
- if (this.list.len + this.static_list.count() == 0) return RouteGroup.Root.initEmpty();
-
- var root = RouteGroup.Root{
- .all = this.all_routes.toOwnedSlice(allocator),
- .index = this.index,
+ if (this.all_routes.items.len == 0) return Routes{
.static = this.static_list,
- .children = std.StringArrayHashMap(RouteGroup).init(this.allocator),
+ .config = config,
+ .allocator = allocator,
};
- var list = this.list.toOwnedSlice();
+ std.sort.sort(*Route, this.all_routes.items, Route.Sorter{}, Route.Sorter.sortByName);
+
+ var route_list = RouteIndex.List{};
+ route_list.setCapacity(allocator, this.all_routes.items.len) catch unreachable;
+
+ var dynamic_start: ?usize = null;
+ var index_id: ?usize = null;
- var routes = list.items(.route);
+ const public_dir_is_in_top_level_dir = strings.startsWith(this.config.dir, this.fs.top_level_dir);
+ for (this.all_routes.items) |route, i| {
+ if (route.param_count > 0 and dynamic_start == null) {
+ dynamic_start = i;
+ }
+
+ if (route.full_hash == index_route_hash) index_id = i;
+
+ route_list.appendAssumeCapacity(.{
+ .name = route.name,
+ .filepath = route.abs_path.slice(),
+ .match_name = route.match_name.slice(),
+ .public_path = route.public_path.slice(),
+ .route = route,
+ .hash = route.full_hash,
+ });
+ Output.prettyErrorln("Route: {s}", .{route.name});
+ }
- if (routes.len > 0) {
- std.sort.sort(*Route, routes, Route.Sorter{}, Route.Sorter.sortByName);
- for (routes) |route| {
- Output.prettyErrorln("\nName: <b>{s}<r>", .{route.name});
+ var dynamic: []*Route = &[_]*Route{};
+ var dynamic_names: []string = &[_]string{};
+ var dynamic_match_names: []string = &[_]string{};
+
+ if (dynamic_start) |dynamic_i| {
+ dynamic = route_list.items(.route)[dynamic_i..];
+ dynamic_names = route_list.items(.name)[dynamic_i..];
+ dynamic_match_names = route_list.items(.match_name)[dynamic_i..];
+
+ if (index_id) |index_i| {
+ if (index_i > dynamic_i) {
+ // Due to the sorting order, the index route can be the last route.
+ // We don't want to attempt to match the index route or different stuff will break.
+ dynamic = dynamic[0 .. dynamic.len - 1];
+ dynamic_names = dynamic_names[0 .. dynamic_names.len - 1];
+ dynamic_match_names = dynamic_match_names[0 .. dynamic_match_names.len - 1];
+ }
}
- // root.insert(allocator, log, routes);
}
- // return root;
- return root;
+ return Routes{
+ .list = route_list,
+ .dynamic = dynamic,
+ .dynamic_names = dynamic_names,
+ .dynamic_match_names = dynamic_match_names,
+ .static = this.static_list,
+ .index = this.index,
+ .config = config,
+ .allocator = allocator,
+ .index_id = index_id,
+ };
}
pub fn load(this: *RouteLoader, comptime ResolverType: type, resolver: *ResolverType, root_dir_info: *const DirInfo) void {
@@ -447,14 +396,22 @@ const RouteLoader = struct {
for (this.config.extensions) |_extname| {
if (strings.eql(extname[1..], _extname)) {
+ // length is extended by one
+ // entry.dir is a string with a trailing slash
+ if (comptime isDebug) {
+ std.debug.assert(entry.dir.ptr[fs.top_level_dir.len - 1] == '/');
+ }
+
+ const public_dir = entry.dir.ptr[fs.top_level_dir.len - 1 .. entry.dir.len];
+
if (Route.parse(
- entry.base_lowercase(),
- // we extend the pointer length by one to get it's slash
- entry.dir.ptr[this.config.dir.len..entry.dir.len],
+ entry.base(),
extname,
entry,
this.log,
this.allocator,
+ public_dir,
+ @truncate(u16, this.config.dir.len - fs.top_level_dir.len),
)) |route| {
this.appendRoute(route);
}
@@ -472,15 +429,19 @@ const RouteLoader = struct {
// it does not currently handle duplicate exact route matches. that's undefined behavior, for now.
pub fn loadRoutes(
this: *Router,
+ log: *Logger.Log,
root_dir_info: *const DirInfo,
comptime ResolverType: type,
resolver: *ResolverType,
- comptime is_root: bool,
-) anyerror!void {}
+) anyerror!void {
+ if (this.loaded_routes) return;
+ this.routes = RouteLoader.loadAll(this.allocator, this.config, log, ResolverType, resolver, root_dir_info);
+ this.loaded_routes = true;
+}
pub const TinyPtr = packed struct {
- offset: u32 = 0,
- len: u32 = 0,
+ offset: u16 = 0,
+ len: u16 = 0,
pub inline fn str(this: TinyPtr, slice: string) string {
return if (this.len > 0) slice[this.offset .. this.offset + this.len] else "";
@@ -490,29 +451,51 @@ pub const TinyPtr = packed struct {
}
pub inline fn eql(a: TinyPtr, b: TinyPtr) bool {
- return @bitCast(u64, a) == @bitCast(u64, b);
+ return @bitCast(u32, a) == @bitCast(u32, b);
}
-};
-pub const Param = struct {
- key: TinyPtr,
- kind: RoutePart.Tag,
- value: TinyPtr,
+ pub fn from(parent: string, in: string) TinyPtr {
+ if (in.len == 0 or parent.len == 0) return TinyPtr{};
- pub const List = std.MultiArrayList(Param);
+ const right = @ptrToInt(in.ptr) + in.len;
+ const end = @ptrToInt(parent.ptr) + parent.len;
+ if (comptime isDebug) {
+ std.debug.assert(end < right);
+ }
+
+ const length = @maximum(end, right) - right;
+ const offset = @maximum(@ptrToInt(in.ptr), @ptrToInt(parent.ptr)) - @ptrToInt(parent.ptr);
+ return TinyPtr{ .offset = @truncate(u16, offset), .len = @truncate(u16, length) };
+ }
};
pub const Route = struct {
+ /// Public display name for the route.
+ /// "/", "/index" is "/"
+ /// "/foo/index.js" becomes "/foo"
+ /// case-sensitive, has leading slash
name: string,
+
+ /// Name used for matching.
+ /// - Omits leading slash
+ /// - Lowercased
+ match_name: PathString,
+
entry: *Fs.FileSystem.Entry,
full_hash: u32,
param_count: u16,
abs_path: PathString,
+ /// URL-safe path for the route's transpiled script relative to project's top level directory
+ /// - It might not share a prefix with the absolute path due to symlinks.
+ /// - It has a leading slash
+ public_path: PathString,
+
pub const Ptr = TinyPtr;
- pub const index_route_name: string = "index";
+ pub const index_route_name: string = "/";
var route_file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ var second_route_file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
pub const Sorter = struct {
const sort_table: [std.math.maxInt(u8)]u8 = brk: {
@@ -526,7 +509,7 @@ pub const Route = struct {
table['['] = 252;
table[']'] = 253;
// of each segment
- table['/'] = 1;
+ table['/'] = 254;
break :brk table;
};
@@ -546,17 +529,24 @@ pub const Route = struct {
}
pub fn sortByName(ctx: @This(), a: *Route, b: *Route) bool {
- return @call(.{ .modifier = .always_inline }, sortByNameString, .{ ctx, a.name, b.name });
+ // ensure that dynamic routes are always at the bottom
+ // this is so we skip looking at static routes when matching dynamic routes
+ // without allocating a new array
+ if (a.param_count > 0 and b.param_count == 0) return false;
+ if (b.param_count > 0 and a.param_count == 0) return true;
+
+ return @call(.{ .modifier = .always_inline }, sortByNameString, .{ ctx, a.match_name.slice(), b.match_name.slice() });
}
};
pub fn parse(
base_: string,
- dir: string,
extname: string,
entry: *Fs.FileSystem.Entry,
log: *Logger.Log,
allocator: *std.mem.Allocator,
+ public_dir_: string,
+ routes_dirname_len: u16,
) ?Route {
var abs_path_str: string = if (entry.abs_path.isEmpty())
""
@@ -565,37 +555,80 @@ pub const Route = struct {
var base = base_[0 .. base_.len - extname.len];
- if (strings.eql(base, "index")) {
- base = "";
- }
-
- var route_name: string = std.mem.trimRight(u8, dir, "/");
-
- var name: string = brk: {
- if (route_name.len == 0) break :brk base;
- _ = strings.copyLowercase(route_name, &route_file_buf);
- route_file_buf[route_name.len] = '/';
- std.mem.copy(u8, route_file_buf[route_name.len + 1 ..], base);
- break :brk route_file_buf[0 .. route_name.len + 1 + base.len];
+ var public_dir = std.mem.trim(u8, public_dir_, "/");
+
+ // this is a path like
+ // "/pages/index.js"
+ // "/pages/foo/index.ts"
+ // "/pages/foo/bar.tsx"
+ // the name we actually store will often be this one
+ var public_path: string = brk: {
+ if (base.len == 0) break :brk public_dir;
+ route_file_buf[0] = '/';
+
+ var buf = route_file_buf[1..];
+
+ std.mem.copy(
+ u8,
+ buf,
+ public_dir,
+ );
+ buf[public_dir.len] = '/';
+ std.mem.copy(u8, buf[public_dir.len + 1 ..], base);
+
+ std.mem.copy(u8, buf[public_dir.len + 1 + base.len ..], extname);
+ break :brk route_file_buf[0 .. 1 + public_dir.len + 1 + base.len + extname.len];
};
- while (name.len > 0 and name[name.len - 1] == '/') {
+ var name = public_path[0 .. public_path.len - extname.len];
+
+ while (name.len > 1 and name[name.len - 1] == '/') {
name = name[0 .. name.len - 1];
}
- name = std.mem.trimLeft(u8, name, "/");
+ name = name[routes_dirname_len..];
+
+ if (strings.endsWith(name, "/index")) {
+ name = name[0 .. name.len - 6];
+ }
+
+ name = std.mem.trimRight(u8, name, "/");
+
+ var match_name: string = name;
var param_count: u16 = 0;
+ const is_index = name.len == 0;
if (name.len > 0) {
param_count = Pattern.validate(
- name,
+ name[1..],
allocator,
log,
) orelse return null;
- name = FileSystem.DirnameStore.instance.append(@TypeOf(name), name) catch unreachable;
+
+ var has_uppercase = false;
+ var name_i: usize = 0;
+ while (!has_uppercase and name_i < public_path.len) : (name_i += 1) {
+ has_uppercase = public_path[name_i] >= 'A' and public_path[name_i] <= 'Z';
+ }
+
+ const name_offset = @ptrToInt(name.ptr) - @ptrToInt(public_path.ptr);
+
+ if (has_uppercase) {
+ public_path = FileSystem.DirnameStore.instance.append(@TypeOf(public_path), public_path) catch unreachable;
+ name = public_path[name_offset..][0..name.len];
+ match_name = FileSystem.DirnameStore.instance.appendLowerCase(@TypeOf(name[1..]), name[1..]) catch unreachable;
+ } else {
+ public_path = FileSystem.DirnameStore.instance.append(@TypeOf(public_path), public_path) catch unreachable;
+ name = public_path[name_offset..][0..name.len];
+ match_name = name[1..];
+ }
+
+ std.debug.assert(match_name[0] != '/');
+ std.debug.assert(name[0] == '/');
} else {
name = Route.index_route_name;
+ match_name = Route.index_route_name;
}
if (abs_path_str.len == 0) {
@@ -631,417 +664,22 @@ pub const Route = struct {
return Route{
.name = name,
.entry = entry,
- .full_hash = @truncate(u32, std.hash.Wyhash.hash(0, abs_path_str)),
+ .public_path = PathString.init(public_path),
+ .match_name = PathString.init(match_name),
+ .full_hash = if (is_index)
+ index_route_hash
+ else
+ @truncate(u32, std.hash.Wyhash.hash(0, name)),
.param_count = param_count,
.abs_path = entry.abs_path,
};
}
};
-// Reference: https://nextjs.org/docs/routing/introduction
-// Examples:
-// - pages/index.js => /
-// - pages/foo.js => /foo
-// - pages/foo/index.js => /foo
-// - pages/foo/[bar] => {/foo/bacon, /foo/bar, /foo/baz, /foo/10293012930}
-// - pages/foo/[...bar] => {/foo/bacon/toast, /foo/bar/what, /foo/baz, /foo/10293012930}
-// Syntax:
-// - [param-name]
-// - Catch All: [...param-name]
-// - Optional Catch All: [[...param-name]]
-// Invalid syntax:
-// - pages/foo/hello-[bar]
-// - pages/foo/[bar]-foo
-pub const RouteMap = struct {
- routes: RouteGroup.Root,
- index: ?u32,
- allocator: *std.mem.Allocator,
- config: Options.RouteConfig,
-
- // This is passed here and propagated through Match
- // We put this here to avoid loading the FrameworkConfig for the client, on the server.
- client_framework_enabled: bool = false,
-
- pub threadlocal var segments_buf: [128]string = undefined;
- pub threadlocal var segments_hash: [128]u32 = undefined;
-
- pub fn routePathLen(this: *const RouteMap, _ptr: u16) u16 {
- return this.appendRoutePath(_ptr, &[_]u8{}, false);
- }
-
- // This is probably really slow
- // But it might be fine because it's mostly looking up within the same array
- // and that array is probably in the cache line
- var ptr_buf: [arbitrary_max_route]u16 = undefined;
- // TODO: skip copying parent dirs when it's another file in the same parent dir
- pub fn appendRoutePath(this: *const RouteMap, tail: u16, buf: []u8, comptime write: bool) u16 {
- var head: u16 = this.routes.items(.parent)[tail];
-
- var ptr_buf_count: i32 = 0;
- var written: u16 = 0;
- while (!(head == Route.top_level_parent)) : (ptr_buf_count += 1) {
- ptr_buf[@intCast(usize, ptr_buf_count)] = head;
- head = this.routes.items(.parent)[head];
- }
-
- var i: usize = @intCast(usize, ptr_buf_count);
- var remain = buf;
- while (i > 0) : (i -= 1) {
- const path = this.routes.items(.path)[
- @intCast(
- usize,
- ptr_buf[i],
- )
- ];
- if (comptime write) {
- std.mem.copy(u8, remain, path);
-
- remain = remain[path.len..];
- remain[0] = std.fs.path.sep;
- remain = remain[1..];
- }
- written += @truncate(u16, path.len + 1);
- }
-
- {
- const path = this.routes.items(.path)[tail];
- if (comptime write) {
- std.mem.copy(u8, remain, path);
- }
- written += @truncate(u16, path.len);
- }
-
- return written;
- }
-
- const MatchContext = struct {
- params: *Param.List,
- segments: []string,
- hashes: []u32,
- map: *RouteMap,
- allocator: *std.mem.Allocator,
- redirect_path: ?string = "",
- url_path: URLPath,
-
- matched_route_buf: []u8 = undefined,
-
- file_path: string = "",
-
- pub fn matchDynamicRoute(
- this: *MatchContext,
- head_i: u16,
- segment_i: u16,
- ) ?Match {
- const start_len = this.params.len;
- var head = this.map.routes.get(head_i);
- const remaining: []string = this.segments[segment_i + 1 ..];
-
- if ((remaining.len > 0 and head.children.len == 0)) {
- return null;
- }
-
- switch (head.part.tag) {
- .exact => {
- // is it the end of an exact match?
- if (!(this.hashes.len > segment_i and this.hashes[segment_i] == head.hash)) {
- return null;
- }
- },
- else => {},
- }
-
- var match_result: Match = undefined;
- if (head.children.len > 0 and remaining.len > 0) {
- var child_i = head.children.offset;
- const last = child_i + head.children.len;
- var matched = false;
- while (child_i < last) : (child_i += 1) {
- if (this.matchDynamicRoute(child_i, segment_i + 1)) |res| {
- match_result = res;
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- this.params.shrinkRetainingCapacity(start_len);
- return null;
- }
- // this is a folder
- } else if (remaining.len == 0 and head.children.len > 0) {
- this.params.shrinkRetainingCapacity(start_len);
- return null;
- } else {
- const entry = head.entry;
- var parts = [_]string{ entry.dir, entry.base() };
- const file_path = Fs.FileSystem.instance.absBuf(&parts, this.matched_route_buf);
-
- match_result = Match{
- .path = head.path,
- .name = Match.nameWithBasename(file_path, this.map.config.dir),
- .params = this.params,
- .hash = head.full_hash,
- .query_string = this.url_path.query_string,
- .pathname = this.url_path.pathname,
- .basename = entry.base(),
- .file_path = file_path,
- };
-
- this.matched_route_buf[match_result.file_path.len] = 0;
- }
-
- // Now that we know for sure the route will match, we append the param
- switch (head.part.tag) {
- .param => {
- // account for the slashes
- var segment_offset: u16 = segment_i;
- for (this.segments[0..segment_i]) |segment| {
- segment_offset += @truncate(u16, segment.len);
- }
- var total_offset: u16 = 0;
-
- var current_i: u16 = head.parent;
- const slices = this.map.routes;
- const names = slices.items(.name);
- const parents = slices.items(.parent);
- while (current_i != Route.top_level_parent) : (current_i = parents[current_i]) {
- total_offset += @truncate(u16, names[current_i].len);
- }
-
- this.params.append(
- this.allocator,
- Param{
- .key = .{ .offset = head.part.name.offset + total_offset + segment_i, .len = head.part.name.len },
- .value = .{ .offset = segment_offset, .len = @truncate(u16, this.segments[segment_i].len) },
- .kind = head.part.tag,
- },
- ) catch unreachable;
- },
- else => {},
- }
-
- return match_result;
- }
- };
-
- // This makes many passes over the list of routes
- // However, most of those passes are basically array.indexOf(number) and then smallerArray.indexOf(number)
- pub fn matchPage(this: *RouteMap, routes_dir: string, file_path_buf: []u8, url_path: URLPath, params: *Param.List) ?Match {
- // Trim trailing slash
- var path = url_path.path;
- var redirect = false;
-
- // Normalize trailing slash
- // "/foo/bar/index/" => "/foo/bar/index"
- if (path.len > 0 and path[path.len - 1] == '/') {
- path = path[0 .. path.len - 1];
- redirect = true;
- }
-
- // Normal case: "/foo/bar/index" => "/foo/bar"
- // Pathological: "/foo/bar/index/index/index/index/index/index" => "/foo/bar"
- // Extremely pathological: "/index/index/index/index/index/index/index" => "index"
- while (strings.endsWith(path, "/index")) {
- path = path[0 .. path.len - "/index".len];
- redirect = true;
- }
-
- if (strings.eqlComptime(path, "index")) {
- path = "";
- redirect = true;
- }
-
- if (strings.eqlComptime(path, ".")) {
- path = "";
- redirect = false;
- }
-
- const routes_slice = this.routes.slice();
-
- if (path.len == 0) {
- if (this.index) |index| {
- const entry = routes_slice.items(.entry)[index];
- const parts = [_]string{ entry.dir, entry.base() };
-
- return Match{
- .params = params,
- .name = routes_slice.items(.name)[index],
- .path = routes_slice.items(.path)[index],
- .pathname = url_path.pathname,
- .basename = entry.base(),
- .hash = index_route_hash,
- .file_path = Fs.FileSystem.instance.absBuf(&parts, file_path_buf),
- .query_string = url_path.query_string,
- .client_framework_enabled = this.client_framework_enabled,
- };
- }
-
- return null;
- }
-
- const full_hash = @truncate(u32, std.hash.Wyhash.hash(0, path));
-
- // Check for an exact match
- // These means there are no params.
- if (std.mem.indexOfScalar(u32, routes_slice.items(.full_hash), full_hash)) |exact_match| {
- const route = this.routes.get(exact_match);
- // It might be a folder with an index route
- // /bacon/index.js => /bacon
- if (route.children.len > 0) {
- const children = routes_slice.items(.hash)[route.children.offset .. route.children.offset + route.children.len];
- for (children) |child_hash, i| {
- if (child_hash == index_route_hash) {
- const entry = routes_slice.items(.entry)[i + route.children.offset];
- const parts = [_]string{ entry.dir, entry.base() };
- const file_path = Fs.FileSystem.instance.absBuf(&parts, file_path_buf);
- return Match{
- .params = params,
- .name = Match.nameWithBasename(file_path, this.config.dir),
- .path = routes_slice.items(.path)[i],
- .pathname = url_path.pathname,
- .basename = entry.base(),
- .hash = child_hash,
- .file_path = file_path,
- .query_string = url_path.query_string,
- .client_framework_enabled = this.client_framework_enabled,
- };
- }
- }
- // It's an exact route, there are no params
- // /foo/bar => /foo/bar.js
- } else {
- const entry = route.entry;
- const parts = [_]string{ entry.dir, entry.base() };
- const file_path = Fs.FileSystem.instance.absBuf(&parts, file_path_buf);
- return Match{
- .params = params,
- .name = Match.nameWithBasename(file_path, this.config.dir),
- .path = route.path,
- .redirect_path = if (redirect) path else null,
- .hash = full_hash,
- .basename = entry.base(),
- .pathname = url_path.pathname,
- .query_string = url_path.query_string,
- .file_path = file_path,
- .client_framework_enabled = this.client_framework_enabled,
- };
- }
- }
-
- var last_slash_i: usize = 0;
- var segments: []string = segments_buf[0..];
- var hashes: []u32 = segments_hash[0..];
- var segment_i: usize = 0;
- var splitter = std.mem.tokenize(u8, path, "/");
- while (splitter.next()) |part| {
- if (part.len == 0 or (part.len == 1 and part[0] == '.')) continue;
- segments[segment_i] = part;
- hashes[segment_i] = @truncate(u32, std.hash.Wyhash.hash(0, part));
- segment_i += 1;
- }
- segments = segments[0..segment_i];
- hashes = hashes[0..segment_i];
-
- // Now, we've established that there is no exact match.
- // Something will be dynamic
- // There are three tricky things about this.
- // 1. It's possible that the correct route is a catch-all route or an optional catch-all route.
- // 2. Given routes like this:
- // * [name]/[id]
- // * foo/[id]
- // If the URL is /foo/123
- // Then the correct route is foo/[id]
- var ctx = MatchContext{
- .params = params,
- .segments = segments,
- .hashes = hashes,
- .map = this,
- .redirect_path = if (redirect) path else null,
- .allocator = this.allocator,
- .url_path = url_path,
- .matched_route_buf = file_path_buf,
- };
-
- // iterate over the top-level routes
- if (ctx.matchDynamicRoute(0, 0)) |_dynamic_route| {
- // route name == the filesystem path relative to the pages dir excluding the file extension
- var dynamic_route = _dynamic_route;
- dynamic_route.client_framework_enabled = this.client_framework_enabled;
- return dynamic_route;
- }
-
- return null;
- }
-};
-
-// This is a u32
-pub const RoutePart = packed struct {
- name: Ptr,
- tag: Tag,
-
- pub fn str(this: RoutePart, name: string) string {
- return switch (this.tag) {
- .exact => name,
- else => name[this.name.offset..][0..this.name.len],
- };
- }
-
- pub const Ptr = packed struct {
- offset: u14,
- len: u14,
- };
-
- pub const Tag = enum(u4) {
- optional_catch_all = 1,
- catch_all = 2,
- param = 3,
- exact = 4,
- };
-
- pub fn parse(base: string) RoutePart {
- std.debug.assert(base.len > 0);
-
- var part = RoutePart{
- .name = Ptr{ .offset = 0, .len = @truncate(u14, base.len) },
- .tag = .exact,
- };
-
- if (base[0] == '[') {
- if (base.len > 1) {
- switch (base[1]) {
- ']' => {},
-
- '[' => {
- // optional catch all
- if (strings.eqlComptime(base[1..std.math.min(base.len, 5)], "[...")) {
- part.name.len = @truncate(u14, std.mem.indexOfScalar(u8, base[5..], ']') orelse return part);
- part.name.offset = 5;
- part.tag = .optional_catch_all;
- }
- },
- '.' => {
- // regular catch all
- if (strings.eqlComptime(base[1..std.math.min(base.len, 4)], "...")) {
- part.name.len = @truncate(u14, std.mem.indexOfScalar(u8, base[4..], ']') orelse return part);
- part.name.offset = 4;
- part.tag = .catch_all;
- }
- },
- else => {
- part.name.len = @truncate(u14, std.mem.indexOfScalar(u8, base[1..], ']') orelse return part);
- part.tag = .param;
- part.name.offset = 1;
- },
- }
- }
- }
-
- return part;
- }
-};
-
threadlocal var params_list: Param.List = undefined;
pub fn match(app: *Router, server: anytype, comptime RequestContextType: type, ctx: *RequestContextType) !void {
+ ctx.matched_route = null;
+
// If there's an extname assume it's an asset and not a page
switch (ctx.url.extname.len) {
0 => {},
@@ -1059,8 +697,7 @@ pub fn match(app: *Router, server: anytype, comptime RequestContextType: type, c
}
params_list.shrinkRetainingCapacity(0);
- var filepath_buf = std.mem.span(&ctx.match_file_path_buf);
- if (app.routes.matchPage(app.config.dir, filepath_buf, ctx.url, &params_list)) |route| {
+ if (app.routes.matchPage(app.config.dir, ctx.url, &params_list)) |route| {
if (route.redirect_path) |redirect| {
try ctx.handleRedirect(redirect);
return;
@@ -1073,7 +710,7 @@ pub fn match(app: *Router, server: anytype, comptime RequestContextType: type, c
}
ctx.matched_route = route;
- RequestContextType.JavaScriptHandler.enqueue(ctx, server, filepath_buf, &params_list) catch {
+ RequestContextType.JavaScriptHandler.enqueue(ctx, server, &params_list) catch {
server.javascript_enabled = false;
};
}
@@ -1121,31 +758,507 @@ pub const Match = struct {
}
};
+const FileSystem = Fs.FileSystem;
+
+const MockRequestContextType = struct {
+ controlled: bool = false,
+ url: URLPath,
+ match_file_path_buf: [1024]u8 = undefined,
+
+ handle_request_called: bool = false,
+ redirect_called: bool = false,
+ matched_route: ?Match = null,
+ has_called_done: bool = false,
+
+ pub fn handleRequest(this: *MockRequestContextType) !void {
+ this.handle_request_called = true;
+ }
+
+ pub fn handleRedirect(this: *MockRequestContextType, pathname: string) !void {
+ this.redirect_called = true;
+ }
+
+ pub const JavaScriptHandler = struct {
+ pub fn enqueue(ctx: *MockRequestContextType, server: *MockServer, params: *Router.Param.List) !void {}
+ };
+};
+
+pub const MockServer = struct {
+ watchloop_handle: ?StoredFileDescriptorType = null,
+ watcher: Watcher = Watcher{},
+
+ pub const Watcher = struct {
+ watchloop_handle: ?StoredFileDescriptorType = null,
+ pub fn start(this: *Watcher) anyerror!void {}
+ };
+};
+
+fn makeTest(cwd_path: string, data: anytype) !void {
+ Output.initTest();
+ std.debug.assert(cwd_path.len > 1 and !strings.eql(cwd_path, "/") and !strings.endsWith(cwd_path, "bun"));
+ const bun_tests_dir = try std.fs.cwd().makeOpenPath("bun-test-scratch", .{ .iterate = true });
+ bun_tests_dir.deleteTree(cwd_path) catch {};
+
+ const cwd = try bun_tests_dir.makeOpenPath(cwd_path, .{ .iterate = true });
+ try cwd.setAsCwd();
+
+ const Data = @TypeOf(data);
+ const fields: []const std.builtin.TypeInfo.StructField = comptime std.meta.fields(Data);
+ inline for (fields) |field| {
+ @setEvalBranchQuota(9999);
+ const value = @field(data, field.name);
+
+ if (std.fs.path.dirname(field.name)) |dir| {
+ try cwd.makePath(dir);
+ }
+ var file = try cwd.createFile(field.name, .{ .truncate = true });
+ try file.writeAll(std.mem.span(value));
+ file.close();
+ }
+}
+
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualStrings = std.testing.expectEqualStrings;
+const expectStr = std.testing.expectEqualStrings;
+const Logger = @import("./logger.zig");
+
+pub const Test = struct {
+ pub fn makeRoutes(comptime testName: string, data: anytype) !Routes {
+ Output.initTest();
+ try makeTest(testName, data);
+ const JSAst = @import("./js_ast.zig");
+ JSAst.Expr.Data.Store.create(default_allocator);
+ JSAst.Stmt.Data.Store.create(default_allocator);
+ var fs = try FileSystem.init1(default_allocator, null);
+ var top_level_dir = fs.top_level_dir;
+
+ var pages_parts = [_]string{ top_level_dir, "pages" };
+ var pages_dir = try Fs.FileSystem.instance.absAlloc(default_allocator, &pages_parts);
+ // _ = try std.fs.makeDirAbsolute(
+ // pages_dir,
+ // );
+ var router = try Router.init(&FileSystem.instance, default_allocator, Options.RouteConfig{
+ .dir = pages_dir,
+ .routes_enabled = true,
+ .extensions = &.{"js"},
+ });
+
+ const Resolver = @import("./resolver/resolver.zig").Resolver;
+ var logger = Logger.Log.init(default_allocator);
+ errdefer {
+ logger.printForLogLevel(Output.errorWriter()) catch {};
+ }
+
+ var opts = Options.BundleOptions{
+ .resolve_mode = .lazy,
+ .platform = .browser,
+ .loaders = undefined,
+ .define = undefined,
+ .log = &logger,
+ .routes = router.config,
+ .entry_points = &.{},
+ .out_extensions = std.StringHashMap(string).init(default_allocator),
+ .transform_options = std.mem.zeroes(Api.TransformOptions),
+ .external = Options.ExternalModules.init(
+ default_allocator,
+ &FileSystem.instance.fs,
+ FileSystem.instance.top_level_dir,
+ &.{},
+ &logger,
+ .browser,
+ ),
+ };
+
+ var resolver = Resolver.init1(default_allocator, &logger, &FileSystem.instance, opts);
+
+ var root_dir = (try resolver.readDirInfo(pages_dir)).?;
+ var entries = root_dir.getEntries().?;
+ return RouteLoader.loadAll(default_allocator, opts.routes, &logger, Resolver, &resolver, root_dir);
+ // try router.loadRoutes(root_dir, Resolver, &resolver, 0, true);
+ // var entry_points = try router.getEntryPoints(default_allocator);
+
+ // try expectEqual(std.meta.fieldNames(@TypeOf(data)).len, entry_points.len);
+ // return router;
+ }
+
+ pub fn make(comptime testName: string, data: anytype) !Router {
+ try makeTest(testName, data);
+ const JSAst = @import("./js_ast.zig");
+ JSAst.Expr.Data.Store.create(default_allocator);
+ JSAst.Stmt.Data.Store.create(default_allocator);
+ var fs = try FileSystem.init1(default_allocator, null);
+ var top_level_dir = fs.top_level_dir;
+
+ var pages_parts = [_]string{ top_level_dir, "pages" };
+ var pages_dir = try Fs.FileSystem.instance.absAlloc(default_allocator, &pages_parts);
+ // _ = try std.fs.makeDirAbsolute(
+ // pages_dir,
+ // );
+ var router = try Router.init(&FileSystem.instance, default_allocator, Options.RouteConfig{
+ .dir = pages_dir,
+ .routes_enabled = true,
+ .extensions = &.{"js"},
+ });
+
+ const Resolver = @import("./resolver/resolver.zig").Resolver;
+ var logger = Logger.Log.init(default_allocator);
+ errdefer {
+ logger.printForLogLevel(Output.errorWriter()) catch {};
+ }
+
+ var opts = Options.BundleOptions{
+ .resolve_mode = .lazy,
+ .platform = .browser,
+ .loaders = undefined,
+ .define = undefined,
+ .log = &logger,
+ .routes = router.config,
+ .entry_points = &.{},
+ .out_extensions = std.StringHashMap(string).init(default_allocator),
+ .transform_options = std.mem.zeroes(Api.TransformOptions),
+ .external = Options.ExternalModules.init(
+ default_allocator,
+ &FileSystem.instance.fs,
+ FileSystem.instance.top_level_dir,
+ &.{},
+ &logger,
+ .browser,
+ ),
+ };
+
+ var resolver = Resolver.init1(default_allocator, &logger, &FileSystem.instance, opts);
+
+ var root_dir = (try resolver.readDirInfo(pages_dir)).?;
+ var entries = root_dir.getEntries().?;
+ try router.loadRoutes(&logger, root_dir, Resolver, &resolver);
+ var entry_points = try router.getEntryPoints();
+
+ try expectEqual(std.meta.fieldNames(@TypeOf(data)).len, entry_points.len);
+ return router;
+ }
+};
+
+test "Route Loader" {
+ var server = MockServer{};
+ var ctx = MockRequestContextType{
+ .url = try URLPath.parse("/hi"),
+ };
+ const fixtures = @import("./test/fixtures.zig");
+ var router = try Test.make("routes-basic", fixtures.github_api_routes_list);
+
+ var parameters = Param.List{};
+ const MatchContext = struct {
+ params: Param.List,
+
+ pub fn empty(this: *@This()) !void {
+ try expectEqual(this.params.len, 0);
+ }
+ };
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/organizations", *MatchContext, &match_ctx);
+ try match_ctx.empty();
+ try expectEqualStrings(route.?.name, "organizations");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/app/installations/", *MatchContext, &match_ctx);
+ try match_ctx.empty();
+ try expectEqualStrings(route.?.name, "app/installations");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/app/installations/123", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "app/installations/[installation_id]");
+ try expectEqualStrings(match_ctx.params.get(0).name, "installation_id");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/codes_of_conduct/", *MatchContext, &match_ctx);
+ try match_ctx.empty();
+ try expectEqualStrings(route.?.name, "codes_of_conduct");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "codes_of_conduct/123", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "codes_of_conduct/[key]");
+ try expectEqualStrings(match_ctx.params.get(0).name, "key");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "codes_of_conduct/123/", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "codes_of_conduct/[key]");
+ try expectEqualStrings(match_ctx.params.get(0).name, "key");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/orgs/123/index", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "orgs/[org]");
+ try expectEqualStrings(match_ctx.params.get(0).name, "org");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/orgs/123/actions/permissions", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "orgs/[org]/actions/permissions");
+ try expectEqualStrings(match_ctx.params.get(0).name, "org");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+ }
+
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/orgs/orgg/teams/teamm/discussions/123/comments/999/reactions", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number]/reactions");
+ try expectEqualStrings(match_ctx.params.get(0).name, "org");
+ try expectEqualStrings(match_ctx.params.get(0).value, "orgg");
+
+ try expectEqualStrings(match_ctx.params.get(1).name, "team_slug");
+ try expectEqualStrings(match_ctx.params.get(1).value, "teamm");
+
+ try expectEqualStrings(match_ctx.params.get(2).name, "discussion_number");
+ try expectEqualStrings(match_ctx.params.get(2).value, "123");
+
+ try expectEqualStrings(match_ctx.params.get(3).name, "comment_number");
+ try expectEqualStrings(match_ctx.params.get(3).value, "999");
+ }
+ {
+ var match_ctx = MatchContext{ .params = .{} };
+ var route = router.match(default_allocator, "/repositories/123/environments/production/not-real", *MatchContext, &match_ctx);
+ try expectEqualStrings(route.?.name, "repositories/[repository_id]/[...jarred-fake-catch-all]");
+ try expectEqualStrings(match_ctx.params.get(0).name, "repository_id");
+ try expectEqualStrings(match_ctx.params.get(0).value, "123");
+
+ try expectEqualStrings(match_ctx.params.get(1).name, "jarred-fake-catch-all");
+ try expectEqualStrings(match_ctx.params.get(1).value, "environments/production/not-real");
+
+ try expectEqual(match_ctx.params.len, 2);
+ }
+}
+
+test "Routes basic" {
+ var server = MockServer{};
+ var ctx = MockRequestContextType{
+ .url = try URLPath.parse("/hi"),
+ };
+
+ var router = try Test.make("routes-basic", .{
+ .@"pages/hi.js" = "//hi",
+ .@"pages/index.js" = "//index",
+ .@"pages/blog/hi.js" = "//blog/hi",
+ });
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expectEqualStrings(ctx.matched_route.?.name, "/hi");
+
+ ctx = MockRequestContextType{
+ .url = try URLPath.parse("/"),
+ };
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expectEqualStrings(ctx.matched_route.?.name, "/");
+
+ ctx = MockRequestContextType{
+ .url = try URLPath.parse("/blog/hi"),
+ };
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expectEqualStrings(ctx.matched_route.?.name, "/blog/hi");
+
+ ctx = MockRequestContextType{
+ .url = try URLPath.parse("/blog/hey"),
+ };
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expect(ctx.matched_route == null);
+
+ ctx = MockRequestContextType{
+ .url = try URLPath.parse("/blog/"),
+ };
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expect(ctx.matched_route == null);
+
+ ctx = MockRequestContextType{
+ .url = try URLPath.parse("/pages/hi"),
+ };
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expect(ctx.matched_route == null);
+}
+
+test "Dynamic routes" {
+ var server = MockServer{};
+ var ctx = MockRequestContextType{
+ .url = try URLPath.parse("/blog/hi"),
+ };
+ var filepath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ var router = try Test.make("routes-dynamic", .{
+ .@"pages/index.js" = "//index.js",
+ .@"pages/blog/hi.js" = "//blog-hi",
+ .@"pages/posts/[id].js" = "//hi",
+ // .@"pages/blog/posts/bacon.js" = "//index",
+ });
+
+ try router.match(&server, MockRequestContextType, &ctx);
+ try expectEqualStrings(ctx.matched_route.?.name, "blog/hi");
+
+ var params = ctx.matched_route.?.paramsIterator();
+ try expect(params.next() == null);
+
+ ctx.matched_route = null;
+
+ ctx.url = try URLPath.parse("/posts/123");
+ try router.match(&server, MockRequestContextType, &ctx);
+
+ params = ctx.matched_route.?.paramsIterator();
+
+ try expectEqualStrings(ctx.matched_route.?.name, "posts/[id]");
+ try expectEqualStrings(params.next().?.rawValue(ctx.matched_route.?.pathname), "123");
+
+ // ctx = MockRequestContextType{
+ // .url = try URLPath.parse("/"),
+ // };
+
+ // try router.match(&server, MockRequestContextType, &ctx);
+ // try expectEqualStrings(ctx.matched_route.name, "index");
+}
+
+test "Pattern" {
+ const pattern = "[dynamic]/static/[dynamic2]/[...catch_all]";
+
+ const dynamic = try Pattern.init(pattern, 0);
+ try expectStr(@tagName(dynamic.value), "dynamic");
+ const static = try Pattern.init(pattern, dynamic.len);
+ try expectStr(@tagName(static.value), "static");
+ const dynamic2 = try Pattern.init(pattern, static.len);
+ try expectStr(@tagName(dynamic2.value), "dynamic");
+ const static2 = try Pattern.init(pattern, dynamic2.len);
+ try expectStr(@tagName(static2.value), "static");
+ const catch_all = try Pattern.init(pattern, static2.len);
+ try expectStr(@tagName(catch_all.value), "catch_all");
+
+ try expectStr(dynamic.value.dynamic.str(pattern), "dynamic");
+ try expectStr(static.value.static, "/static/");
+ try expectStr(dynamic2.value.dynamic.str(pattern), "dynamic2");
+ try expectStr(static2.value.static, "/");
+ try expectStr(catch_all.value.catch_all.str(pattern), "catch_all");
+}
+
const Pattern = struct {
value: Value,
- len: u32 = 0,
-
- // pub fn match(path: string, name: string, params: *para) bool {
- // var offset: u32 = 0;
- // var path_i: u32 = 0;
- // while (offset < name.len) {
- // var pattern = Pattern.init(name, 0) catch unreachable;
- // var path_ = path[path_i..];
-
- // switch (pattern.value) {
- // .static => |str| {
- // if (!strings.eql(str, path_[0..str.len])) {
- // return false;
- // }
-
- // path_ = path_[str.len..];
- // offset = pattern.len;
- // },
- // }
- // }
-
- // return true;
- // }
+ len: RoutePathInt = 0,
+
+ /// Match a filesystem route pattern to a URL path.
+ pub fn match(
+ // `path` must be lowercased and have no leading slash
+ path: string,
+ /// case-sensitive, must not have a leading slash
+ name: string,
+ /// case-insensitive, must not have a leading slash
+ match_name: string,
+ allocator: *std.mem.Allocator,
+ comptime ParamsListType: type,
+ params: ParamsListType,
+ comptime allow_optional_catch_all: bool,
+ ) bool {
+ var offset: RoutePathInt = 0;
+ var path_ = path;
+ while (offset < name.len) {
+ var pattern = Pattern.init(match_name, offset) catch unreachable;
+ offset = pattern.len;
+
+ switch (pattern.value) {
+ .static => |str| {
+ const segment = path_[0 .. std.mem.indexOfScalar(u8, path_, '/') orelse path_.len];
+ if (!str.eql(segment)) {
+ params.shrinkRetainingCapacity(0);
+ return false;
+ }
+
+ path_ = if (segment.len < path_.len)
+ path_[segment.len + 1 ..]
+ else
+ "";
+
+ if (path_.len == 0 and pattern.isEnd(name)) return true;
+ },
+ .dynamic => |dynamic| {
+ if (std.mem.indexOfScalar(u8, path_, '/')) |i| {
+ params.append(allocator, .{
+ .name = dynamic,
+ .value = path,
+ }) catch unreachable;
+ path_ = path_[i + 1 ..];
+
+ if (pattern.isEnd(name)) {
+ params.shrinkRetainingCapacity(0);
+ return false;
+ }
+
+ continue;
+ } else if (pattern.isEnd(name)) {
+ params.append(allocator, .{
+ .name = dynamic.str(name),
+ .value = path_,
+ }) catch unreachable;
+ return true;
+ } else if (comptime allow_optional_catch_all) {
+ pattern = Pattern.init(match_name, offset) catch unreachable;
+
+ if (pattern.value == .optional_catch_all) {
+ params.append(allocator, .{
+ .name = dynamic.str(name),
+ .value = path_,
+ }) catch unreachable;
+ path_ = "";
+ }
+
+ return true;
+ }
+
+ if (comptime !allow_optional_catch_all) {
+ return true;
+ }
+ },
+ .catch_all => |dynamic| {
+ if (path_.len > 0) {
+ params.append(allocator, .{
+ .name = dynamic.str(name),
+ .value = path_,
+ }) catch unreachable;
+ return true;
+ }
+
+ return false;
+ },
+ .optional_catch_all => |dynamic| {
+ if (comptime allow_optional_catch_all) {
+ if (path_.len > 0) params.append(allocator, .{
+ .name = dynamic.str(name),
+ .value = path_,
+ }) catch unreachable;
+
+ return true;
+ }
+
+ return false;
+ },
+ }
+ }
+
+ return false;
+ }
/// Validate a Route pattern, returning the number of route parameters.
/// `null` means invalid. Error messages are logged.
@@ -1164,7 +1277,7 @@ const Pattern = struct {
}
var count: u16 = 0;
- var offset: u32 = 0;
+ var offset: RoutePathInt = 0;
std.debug.assert(input.len > 0);
const end = @truncate(u32, input.len - 1);
@@ -1249,22 +1362,24 @@ const Pattern = struct {
PatternMissingClosingBracket,
};
- pub fn init(input: string, offset_: u32) PatternParseError!Pattern {
+ const RoutePathInt = u16;
+
+ pub fn init(input: string, offset_: RoutePathInt) PatternParseError!Pattern {
return initMaybeHash(input, offset_, true);
}
pub fn isEnd(this: Pattern, input: string) bool {
- return @as(usize, this.len) >= input.len;
+ return @as(usize, this.len) >= input.len - 1;
}
- pub fn initUnhashed(input: string, offset_: u32) PatternParseError!Pattern {
+ pub fn initUnhashed(input: string, offset_: RoutePathInt) PatternParseError!Pattern {
return initMaybeHash(input, offset_, false);
}
- inline fn initMaybeHash(input: string, offset_: u32, comptime do_hash: bool) PatternParseError!Pattern {
+ inline fn initMaybeHash(input: string, offset_: RoutePathInt, comptime do_hash: bool) PatternParseError!Pattern {
const initHashedString = if (comptime do_hash) HashedString.init else HashedString.initNoHash;
- var offset: u32 = offset_;
+ var offset: RoutePathInt = offset_;
while (input.len > @as(usize, offset) and input[offset] == '/') {
offset += 1;
@@ -1272,20 +1387,20 @@ const Pattern = struct {
if (input.len == 0 or input.len <= @as(usize, offset)) return Pattern{
.value = .{ .static = HashedString.empty },
- .len = @truncate(u32, @minimum(input.len, @as(usize, offset))),
+ .len = @truncate(RoutePathInt, @minimum(input.len, @as(usize, offset))),
};
- var i: u32 = offset;
+ var i: RoutePathInt = offset;
var tag = Tag.static;
- const end = @intCast(u32, input.len - 1);
+ const end = @intCast(RoutePathInt, input.len - 1);
if (offset == end) return Pattern{ .len = offset, .value = .{ .static = HashedString.empty } };
while (i <= end) : (i += 1) {
switch (input[i]) {
'/' => {
- return Pattern{ .len = i, .value = .{ .static = initHashedString(input[offset..i]) } };
+ return Pattern{ .len = @minimum(i + 1, end), .value = .{ .static = initHashedString(input[offset..i]) } };
},
'[' => {
if (i > offset) {
@@ -1312,9 +1427,11 @@ const Pattern = struct {
return error.InvalidOptionalCatchAllRoute;
}
+ i += 1;
+
const catch_all_dot_start = i;
if (!strings.eqlComptimeIgnoreLen(input[i..][0..3], "...")) return error.InvalidOptionalCatchAllRoute;
- i += 4;
+ i += 3;
param.offset = i;
},
'.' => {
@@ -1345,15 +1462,14 @@ const Pattern = struct {
i += 1;
if (tag == Tag.optional_catch_all) {
- i += 1;
-
if (input[i] != ']') return error.PatternMissingClosingBracket;
+ i += 1;
}
if (@enumToInt(tag) > @enumToInt(Tag.dynamic) and i <= end) return error.CatchAllMustBeAtTheEnd;
return Pattern{
- .len = @minimum(end, i),
+ .len = @minimum(i + 1, end),
.value = switch (tag) {
.dynamic => .{
.dynamic = param,
@@ -1394,541 +1510,155 @@ const Pattern = struct {
};
};
-const FileSystem = Fs.FileSystem;
-
-const MockRequestContextType = struct {
- controlled: bool = false,
- url: URLPath,
- match_file_path_buf: [1024]u8 = undefined,
-
- handle_request_called: bool = false,
- redirect_called: bool = false,
- matched_route: ?Match = null,
- has_called_done: bool = false,
-
- pub fn handleRequest(this: *MockRequestContextType) !void {
- this.handle_request_called = true;
- }
-
- pub fn handleRedirect(this: *MockRequestContextType, pathname: string) !void {
- this.redirect_called = true;
- }
+test "Pattern Match" {
+ Output.initTest();
+ const Entry = Param;
- pub const JavaScriptHandler = struct {
- pub fn enqueue(ctx: *MockRequestContextType, server: *MockServer, filepath_buf: []u8, params: *Router.Param.List) !void {}
- };
-};
-
-pub const MockServer = struct {
- watchloop_handle: ?StoredFileDescriptorType = null,
- watcher: Watcher = Watcher{},
-
- pub const Watcher = struct {
- watchloop_handle: ?StoredFileDescriptorType = null,
- pub fn start(this: *Watcher) anyerror!void {}
- };
-};
-
-fn makeTest(cwd_path: string, data: anytype) !void {
- std.debug.assert(cwd_path.len > 1 and !strings.eql(cwd_path, "/") and !strings.endsWith(cwd_path, "bun"));
- const bun_tests_dir = try std.fs.cwd().makeOpenPath("bun-test-scratch", .{ .iterate = true });
- bun_tests_dir.deleteTree(cwd_path) catch {};
-
- const cwd = try bun_tests_dir.makeOpenPath(cwd_path, .{ .iterate = true });
- try cwd.setAsCwd();
-
- const Data = @TypeOf(data);
- const fields: []const std.builtin.TypeInfo.StructField = comptime std.meta.fields(Data);
- inline for (fields) |field| {
- @setEvalBranchQuota(9999);
- const value = @field(data, field.name);
-
- if (std.fs.path.dirname(field.name)) |dir| {
- try cwd.makePath(dir);
- }
- var file = try cwd.createFile(field.name, .{ .truncate = true });
- try file.writeAll(std.mem.span(value));
- file.close();
- }
-}
-
-const expect = std.testing.expect;
-const expectEqual = std.testing.expectEqual;
-const expectEqualStrings = std.testing.expectEqualStrings;
-const expectStr = std.testing.expectEqualStrings;
-const Logger = @import("./logger.zig");
-
-pub const Test = struct {
- pub fn makeRoot(comptime testName: string, data: anytype) !RouteGroup.Root {
- try makeTest(testName, data);
- const JSAst = @import("./js_ast.zig");
- JSAst.Expr.Data.Store.create(default_allocator);
- JSAst.Stmt.Data.Store.create(default_allocator);
- var fs = try FileSystem.init1(default_allocator, null);
- var top_level_dir = fs.top_level_dir;
-
- var pages_parts = [_]string{ top_level_dir, "pages" };
- var pages_dir = try Fs.FileSystem.instance.absAlloc(default_allocator, &pages_parts);
- // _ = try std.fs.makeDirAbsolute(
- // pages_dir,
- // );
- var router = try Router.init(&FileSystem.instance, default_allocator, Options.RouteConfig{
- .dir = pages_dir,
- .routes_enabled = true,
- .extensions = &.{"js"},
- });
- Output.initTest();
-
- const Resolver = @import("./resolver/resolver.zig").Resolver;
- var logger = Logger.Log.init(default_allocator);
- errdefer {
- logger.printForLogLevel(Output.errorWriter()) catch {};
- }
-
- var opts = Options.BundleOptions{
- .resolve_mode = .lazy,
- .platform = .browser,
- .loaders = undefined,
- .define = undefined,
- .log = &logger,
- .routes = router.config,
- .entry_points = &.{},
- .out_extensions = std.StringHashMap(string).init(default_allocator),
- .transform_options = std.mem.zeroes(Api.TransformOptions),
- .external = Options.ExternalModules.init(
- default_allocator,
- &FileSystem.instance.fs,
- FileSystem.instance.top_level_dir,
- &.{},
- &logger,
- .browser,
- ),
- };
-
- var resolver = Resolver.init1(default_allocator, &logger, &FileSystem.instance, opts);
-
- var root_dir = (try resolver.readDirInfo(pages_dir)).?;
- var entries = root_dir.getEntries().?;
- return RouteLoader.loadAll(default_allocator, opts.routes, &logger, Resolver, &resolver, root_dir);
- // try router.loadRoutes(root_dir, Resolver, &resolver, 0, true);
- // var entry_points = try router.getEntryPoints(default_allocator);
-
- // try expectEqual(std.meta.fieldNames(@TypeOf(data)).len, entry_points.len);
- // return router;
- }
-};
-
-test "Route Loader" {
- var server = MockServer{};
- var ctx = MockRequestContextType{
- .url = try URLPath.parse("/hi"),
- };
- var router = try Test.makeRoot("routes-basic", github_api_routes_list);
-}
-
-test "Routes basic" {
- var server = MockServer{};
- var ctx = MockRequestContextType{
- .url = try URLPath.parse("/hi"),
- };
- var router = try Test.make("routes-basic", .{
- .@"pages/hi.js" = "//hi",
- .@"pages/index.js" = "//index",
- .@"pages/blog/hi.js" = "//blog/hi",
- });
- try router.match(&server, MockRequestContextType, &ctx);
- try expectEqualStrings(ctx.matched_route.?.name, "hi");
-
- ctx = MockRequestContextType{
- .url = try URLPath.parse("/"),
- };
+ const regular_list = .{
+ .@"404" = .{
+ "404",
+ &[_]Entry{},
+ },
+ .@"[teamSlug]" = .{
+ "value",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "value" },
+ },
+ },
+ .@"hi/hello/[teamSlug]" = .{
+ "hi/hello/123",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "123" },
+ },
+ },
+ .@"hi/[teamSlug]/hello" = .{
+ "hi/123/hello",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "123" },
+ },
+ },
+ .@"[teamSlug]/hi/hello" = .{
+ "123/hi/hello",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "123" },
+ },
+ },
+ .@"[teamSlug]/[project]" = .{
+ "team/bacon",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "bacon" },
+ },
+ },
+ .@"lemon/[teamSlug]/[project]" = .{
+ "lemon/team/bacon",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "bacon" },
+ },
+ },
+ .@"[teamSlug]/[project]/lemon" = .{
+ "team/bacon/lemon",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "bacon" },
+ },
+ },
+ .@"[teamSlug]/lemon/[project]" = .{
+ "team/lemon/lemon",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "lemon" },
+ },
+ },
- try router.match(&server, MockRequestContextType, &ctx);
- try expectEqualStrings(ctx.matched_route.?.name, "/index");
+ .@"[teamSlug]/lemon/[...project]" = .{
+ "team/lemon/lemon-bacon-cheese/wow/brocollini",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "lemon-bacon-cheese/wow/brocollini" },
+ },
+ },
- ctx = MockRequestContextType{
- .url = try URLPath.parse("/blog/hi"),
+ .@"[teamSlug]/lemon/[project]/[[...slug]]" = .{
+ "team/lemon/lemon/slugggg",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "lemon" },
+ .{ .name = "slug", .value = "slugggg" },
+ },
+ },
};
- try router.match(&server, MockRequestContextType, &ctx);
- try expectEqualStrings(ctx.matched_route.?.name, "blog/hi");
-
- ctx = MockRequestContextType{
- .url = try URLPath.parse("/blog/hey"),
- };
+ const optional_catch_all = .{
+ .@"404" = .{
+ "404",
+ &[_]Entry{},
+ },
+ .@"404/[[...slug]]" = .{
+ "404",
+ &[_]Entry{},
+ },
- try router.match(&server, MockRequestContextType, &ctx);
- try expect(ctx.matched_route == null);
+ .@"404a/[[...slug]]" = .{
+ "404a",
+ &[_]Entry{},
+ },
- ctx = MockRequestContextType{
- .url = try URLPath.parse("/blog/"),
+ .@"[teamSlug]/lemon/[project]/[[...slug]]" = .{
+ "team/lemon/lemon/slugggg",
+ &[_]Entry{
+ .{ .name = "teamSlug", .value = "team" },
+ .{ .name = "project", .value = "lemon" },
+ .{ .name = "slug", .value = "slugggg" },
+ },
+ },
};
- try router.match(&server, MockRequestContextType, &ctx);
- try expect(ctx.matched_route == null);
-
- ctx = MockRequestContextType{
- .url = try URLPath.parse("/pages/hi"),
- };
+ const TestList = struct {
+ pub fn run(comptime list: anytype) usize {
+ const ParamListType = std.MultiArrayList(Entry);
+ var parameters = ParamListType{};
+ var failures: usize = 0;
+ inline for (comptime std.meta.fieldNames(@TypeOf(list))) |pattern| {
+ parameters.shrinkRetainingCapacity(0);
+
+ const part = comptime @field(list, pattern);
+ const pathname = part.@"0";
+ const entries = part.@"1";
+ fail: {
+ if (!Pattern.match(pathname, pattern, pattern, default_allocator, *ParamListType, &parameters, true)) {
+ Output.prettyErrorln("Expected pattern <b>\"{s}\"<r> to match <b>\"{s}\"<r>", .{ pattern, pathname });
+ failures += 1;
+ break :fail;
+ }
- try router.match(&server, MockRequestContextType, &ctx);
- try expect(ctx.matched_route == null);
-}
+ if (comptime entries.len > 0) {
+ for (parameters.items(.name)) |entry_name, i| {
+ if (!strings.eql(entry_name, entries[i].name)) {
+ failures += 1;
+ Output.prettyErrorln("{s} -- Expected name <b>\"{s}\"<r> but received <b>\"{s}\"<r> for path {s}", .{ pattern, entries[i].name, parameters.get(i).name, pathname });
+ break :fail;
+ }
+ if (!strings.eql(parameters.get(i).value, entries[i].value)) {
+ failures += 1;
+ Output.prettyErrorln("{s} -- Expected value <b>\"{s}\"<r> but received <b>\"{s}\"<r> for path {s}", .{ pattern, entries[i].value, parameters.get(i).value, pathname });
+ break :fail;
+ }
+ }
+ }
-test "Dynamic routes" {
- var server = MockServer{};
- var ctx = MockRequestContextType{
- .url = try URLPath.parse("/blog/hi"),
+ if (parameters.len != entries.len) {
+ Output.prettyErrorln("Expected parameter count for <b>\"{s}\"<r> to match <b>\"{s}\"<r>", .{ pattern, pathname });
+ failures += 1;
+ break :fail;
+ }
+ }
+ }
+ return failures;
+ }
};
- var filepath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- var router = try Test.make("routes-dynamic", .{
- .@"pages/index.js" = "//index.js",
- .@"pages/blog/hi.js" = "//blog-hi",
- .@"pages/posts/[id].js" = "//hi",
- // .@"pages/blog/posts/bacon.js" = "//index",
- });
-
- try router.match(&server, MockRequestContextType, &ctx);
- try expectEqualStrings(ctx.matched_route.?.name, "blog/hi");
-
- var params = ctx.matched_route.?.paramsIterator();
- try expect(params.next() == null);
-
- ctx.matched_route = null;
-
- ctx.url = try URLPath.parse("/posts/123");
- try router.match(&server, MockRequestContextType, &ctx);
-
- params = ctx.matched_route.?.paramsIterator();
-
- try expectEqualStrings(ctx.matched_route.?.name, "/posts/[id]");
- try expectEqualStrings(params.next().?.rawValue(ctx.matched_route.?.pathname), "123");
-
- // ctx = MockRequestContextType{
- // .url = try URLPath.parse("/"),
- // };
-
- // try router.match(&server, MockRequestContextType, &ctx);
- // try expectEqualStrings(ctx.matched_route.name, "index");
-}
-
-test "Pattern" {
- const pattern = "[dynamic]/static/[dynamic2]/[...catch_all]";
-
- const dynamic = try Pattern.init(pattern, 0);
- try expectStr(@tagName(dynamic.value), "dynamic");
- const static = try Pattern.init(pattern, dynamic.len);
- try expectStr(@tagName(static.value), "static");
- const dynamic2 = try Pattern.init(pattern, static.len);
- try expectStr(@tagName(dynamic2.value), "dynamic");
- const static2 = try Pattern.init(pattern, dynamic2.len);
- try expectStr(@tagName(static2.value), "static");
- const catch_all = try Pattern.init(pattern, static2.len);
- try expectStr(@tagName(catch_all.value), "catch_all");
- try expectStr(dynamic.value.dynamic.str(pattern), "dynamic");
- try expectStr(static.value.static, "/static/");
- try expectStr(dynamic2.value.dynamic.str(pattern), "dynamic2");
- try expectStr(static2.value.static, "/");
- try expectStr(catch_all.value.catch_all.str(pattern), "catch_all");
+ if (TestList.run(regular_list) > 0) try expect(false);
+ if (TestList.run(optional_catch_all) > 0) try expect(false);
}
-
-const github_api_routes_list = .{
- .@"pages/[...catch-all-at-root].js" = "//pages/[...catch-all-at-root].js",
- .@"pages/index.js" = "//pages/index.js",
- .@"pages/app.js" = "//pages/app.js",
- .@"pages/app/installations.js" = "//pages/app/installations.js",
- .@"pages/app/installations/[installation_id].js" = "//pages/app/installations/[installation_id].js",
- .@"pages/apps/[app_slug].js" = "//pages/apps/[app_slug].js",
- .@"pages/codes_of_conduct.js" = "//pages/codes_of_conduct.js",
- .@"pages/codes_of_conduct/[key].js" = "//pages/codes_of_conduct/[key].js",
- .@"pages/emojis.js" = "//pages/emojis.js",
- .@"pages/events.js" = "//pages/events.js",
- .@"pages/feeds.js" = "//pages/feeds.js",
- .@"pages/gitignore/templates.js" = "//pages/gitignore/templates.js",
- .@"pages/gitignore/templates/[name].js" = "//pages/gitignore/templates/[name].js",
- .@"pages/installation/repositories.js" = "//pages/installation/repositories.js",
- .@"pages/licenses.js" = "//pages/licenses.js",
- .@"pages/licenses/[license].js" = "//pages/licenses/[license].js",
- .@"pages/meta.js" = "//pages/meta.js",
- .@"pages/networks/[owner]/[repo]/events.js" = "//pages/networks/[owner]/[repo]/events.js",
- .@"pages/octocat.js" = "//pages/octocat.js",
- .@"pages/organizations.js" = "//pages/organizations.js",
- .@"pages/orgs/[org]/index.js" = "//pages/orgs/[org].js",
- .@"pages/orgs/[org]/actions/permissions.js" = "//pages/orgs/[org]/actions/permissions.js",
- .@"pages/orgs/[org]/actions/permissions/repositories.js" = "//pages/orgs/[org]/actions/permissions/repositories.js",
- .@"pages/orgs/[org]/actions/permissions/selected-actions.js" = "//pages/orgs/[org]/actions/permissions/selected-actions.js",
- .@"pages/orgs/[org]/actions/runner-groups.js" = "//pages/orgs/[org]/actions/runner-groups.js",
- .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id].js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id].js",
- .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id]/repositories.js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id]/repositories.js",
- .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id]/runners.js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id]/runners.js",
- .@"pages/orgs/[org]/actions/runners.js" = "//pages/orgs/[org]/actions/runners.js",
- .@"pages/orgs/[org]/actions/runners/[runner_id].js" = "//pages/orgs/[org]/actions/runners/[runner_id].js",
- .@"pages/orgs/[org]/actions/runners/downloads.js" = "//pages/orgs/[org]/actions/runners/downloads.js",
- .@"pages/orgs/[org]/actions/secrets.js" = "//pages/orgs/[org]/actions/secrets.js",
- .@"pages/orgs/[org]/actions/secrets/[secret_name].js" = "//pages/orgs/[org]/actions/secrets/[secret_name].js",
- .@"pages/orgs/[org]/actions/secrets/[secret_name]/repositories.js" = "//pages/orgs/[org]/actions/secrets/[secret_name]/repositories.js",
- .@"pages/orgs/[org]/actions/secrets/public-key.js" = "//pages/orgs/[org]/actions/secrets/public-key.js",
- .@"pages/orgs/[org]/audit-log.js" = "//pages/orgs/[org]/audit-log.js",
- .@"pages/orgs/[org]/blocks.js" = "//pages/orgs/[org]/blocks.js",
- .@"pages/orgs/[org]/blocks/[username].js" = "//pages/orgs/[org]/blocks/[username].js",
- .@"pages/orgs/[org]/credential-authorizations.js" = "//pages/orgs/[org]/credential-authorizations.js",
- .@"pages/orgs/[org]/events.js" = "//pages/orgs/[org]/events.js",
- .@"pages/orgs/[org]/external-group/[group_id].js" = "//pages/orgs/[org]/external-group/[group_id].js",
- .@"pages/orgs/[org]/external-groups.js" = "//pages/orgs/[org]/external-groups.js",
- .@"pages/orgs/[org]/failed_invitations.js" = "//pages/orgs/[org]/failed_invitations.js",
- .@"pages/orgs/[org]/hooks.js" = "//pages/orgs/[org]/hooks.js",
- .@"pages/orgs/[org]/hooks/[hook_id].js" = "//pages/orgs/[org]/hooks/[hook_id].js",
- .@"pages/orgs/[org]/hooks/[hook_id]/config.js" = "//pages/orgs/[org]/hooks/[hook_id]/config.js",
- .@"pages/orgs/[org]/hooks/[hook_id]/deliveries.js" = "//pages/orgs/[org]/hooks/[hook_id]/deliveries.js",
- .@"pages/orgs/[org]/hooks/[hook_id]/deliveries/[delivery_id].js" = "//pages/orgs/[org]/hooks/[hook_id]/deliveries/[delivery_id].js",
- .@"pages/orgs/[org]/installations.js" = "//pages/orgs/[org]/installations.js",
- .@"pages/orgs/[org]/interaction-limits.js" = "//pages/orgs/[org]/interaction-limits.js",
- .@"pages/orgs/[org]/invitations.js" = "//pages/orgs/[org]/invitations.js",
- .@"pages/orgs/[org]/invitations/[invitation_id]/teams.js" = "//pages/orgs/[org]/invitations/[invitation_id]/teams.js",
- .@"pages/orgs/[org]/members.js" = "//pages/orgs/[org]/members.js",
- .@"pages/orgs/[org]/members/[username].js" = "//pages/orgs/[org]/members/[username].js",
- .@"pages/orgs/[org]/memberships/[username].js" = "//pages/orgs/[org]/memberships/[username].js",
- .@"pages/orgs/[org]/outside_collaborators.js" = "//pages/orgs/[org]/outside_collaborators.js",
- .@"pages/orgs/[org]/projects.js" = "//pages/orgs/[org]/projects.js",
- .@"pages/orgs/[org]/public_members.js" = "//pages/orgs/[org]/public_members.js",
- .@"pages/orgs/[org]/public_members/[username].js" = "//pages/orgs/[org]/public_members/[username].js",
- .@"pages/orgs/[org]/repos.js" = "//pages/orgs/[org]/repos.js",
- .@"pages/orgs/[org]/secret-scanning/alerts.js" = "//pages/orgs/[org]/secret-scanning/alerts.js",
- .@"pages/orgs/[org]/team-sync/groups.js" = "//pages/orgs/[org]/team-sync/groups.js",
- .@"pages/orgs/[org]/teams.js" = "//pages/orgs/[org]/teams.js",
- .@"pages/orgs/[org]/teams/[team_slug].js" = "//pages/orgs/[org]/teams/[team_slug].js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions.js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number].js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number].js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments.js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number].js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number].js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number]/reactions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number]/reactions.js",
- .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/reactions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/reactions.js",
- .@"pages/orgs/[org]/teams/[team_slug]/invitations.js" = "//pages/orgs/[org]/teams/[team_slug]/invitations.js",
- .@"pages/orgs/[org]/teams/[team_slug]/members.js" = "//pages/orgs/[org]/teams/[team_slug]/members.js",
- .@"pages/orgs/[org]/teams/[team_slug]/memberships/[username].js" = "//pages/orgs/[org]/teams/[team_slug]/memberships/[username].js",
- .@"pages/orgs/[org]/teams/[team_slug]/projects.js" = "//pages/orgs/[org]/teams/[team_slug]/projects.js",
- .@"pages/orgs/[org]/teams/[team_slug]/projects/[project_id].js" = "//pages/orgs/[org]/teams/[team_slug]/projects/[project_id].js",
- .@"pages/orgs/[org]/teams/[team_slug]/repos.js" = "//pages/orgs/[org]/teams/[team_slug]/repos.js",
- .@"pages/orgs/[org]/teams/[team_slug]/repos/[owner]/[repo].js" = "//pages/orgs/[org]/teams/[team_slug]/repos/[owner]/[repo].js",
- .@"pages/orgs/[org]/teams/[team_slug]/teams.js" = "//pages/orgs/[org]/teams/[team_slug]/teams.js",
- .@"pages/projects/[project_id].js" = "//pages/projects/[project_id].js",
- .@"pages/projects/[project_id]/collaborators.js" = "//pages/projects/[project_id]/collaborators.js",
- .@"pages/projects/[project_id]/collaborators/[username]/permission.js" = "//pages/projects/[project_id]/collaborators/[username]/permission.js",
- .@"pages/projects/[project_id]/columns.js" = "//pages/projects/[project_id]/columns.js",
- .@"pages/projects/columns/[column_id].js" = "//pages/projects/columns/[column_id].js",
- .@"pages/projects/columns/[column_id]/cards.js" = "//pages/projects/columns/[column_id]/cards.js",
- .@"pages/projects/columns/cards/[card_id].js" = "//pages/projects/columns/cards/[card_id].js",
- .@"pages/rate_limit.js" = "//pages/rate_limit.js",
- .@"pages/repos/[owner]/[repo].js" = "//pages/repos/[owner]/[repo].js",
- .@"pages/repos/[owner]/[repo]/actions/artifacts.js" = "//pages/repos/[owner]/[repo]/actions/artifacts.js",
- .@"pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id].js" = "//pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id].js",
- .@"pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id]/[archive_format].js" = "//pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id]/[archive_format].js",
- .@"pages/repos/[owner]/[repo]/actions/jobs/[job_id].js" = "//pages/repos/[owner]/[repo]/actions/jobs/[job_id].js",
- .@"pages/repos/[owner]/[repo]/actions/jobs/[job_id]/logs.js" = "//pages/repos/[owner]/[repo]/actions/jobs/[job_id]/logs.js",
- .@"pages/repos/[owner]/[repo]/actions/permissions.js" = "//pages/repos/[owner]/[repo]/actions/permissions.js",
- .@"pages/repos/[owner]/[repo]/actions/permissions/selected-actions.js" = "//pages/repos/[owner]/[repo]/actions/permissions/selected-actions.js",
- .@"pages/repos/[owner]/[repo]/actions/runners.js" = "//pages/repos/[owner]/[repo]/actions/runners.js",
- .@"pages/repos/[owner]/[repo]/actions/runners/[runner_id].js" = "//pages/repos/[owner]/[repo]/actions/runners/[runner_id].js",
- .@"pages/repos/[owner]/[repo]/actions/runners/downloads.js" = "//pages/repos/[owner]/[repo]/actions/runners/downloads.js",
- .@"pages/repos/[owner]/[repo]/actions/runs.js" = "//pages/repos/[owner]/[repo]/actions/runs.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id].js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id].js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/approvals.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/approvals.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/artifacts.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/artifacts.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number].js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number].js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/jobs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/jobs.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/logs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/logs.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/jobs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/jobs.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/logs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/logs.js",
- .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/pending_deployments.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/pending_deployments.js",
- .@"pages/repos/[owner]/[repo]/actions/secrets.js" = "//pages/repos/[owner]/[repo]/actions/secrets.js",
- .@"pages/repos/[owner]/[repo]/actions/secrets/[secret_name].js" = "//pages/repos/[owner]/[repo]/actions/secrets/[secret_name].js",
- .@"pages/repos/[owner]/[repo]/actions/secrets/public-key.js" = "//pages/repos/[owner]/[repo]/actions/secrets/public-key.js",
- .@"pages/repos/[owner]/[repo]/actions/workflows.js" = "//pages/repos/[owner]/[repo]/actions/workflows.js",
- .@"pages/repos/[owner]/[repo]/actions/workflows/[workflow_id].js" = "//pages/repos/[owner]/[repo]/actions/workflows/[workflow_id].js",
- .@"pages/repos/[owner]/[repo]/actions/workflows/[workflow_id]/runs.js" = "//pages/repos/[owner]/[repo]/actions/workflows/[workflow_id]/runs.js",
- .@"pages/repos/[owner]/[repo]/assignees.js" = "//pages/repos/[owner]/[repo]/assignees.js",
- .@"pages/repos/[owner]/[repo]/assignees/[assignee].js" = "//pages/repos/[owner]/[repo]/assignees/[assignee].js",
- .@"pages/repos/[owner]/[repo]/autolinks.js" = "//pages/repos/[owner]/[repo]/autolinks.js",
- .@"pages/repos/[owner]/[repo]/autolinks/[autolink_id].js" = "//pages/repos/[owner]/[repo]/autolinks/[autolink_id].js",
- .@"pages/repos/[owner]/[repo]/branches.js" = "//pages/repos/[owner]/[repo]/branches.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch].js" = "//pages/repos/[owner]/[repo]/branches/[branch].js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/enforce_admins.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/enforce_admins.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_pull_request_reviews.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_pull_request_reviews.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_signatures.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_signatures.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks/contexts.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks/contexts.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/apps.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/apps.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/teams.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/teams.js",
- .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/users.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/users.js",
- .@"pages/repos/[owner]/[repo]/check-runs/[check_run_id].js" = "//pages/repos/[owner]/[repo]/check-runs/[check_run_id].js",
- .@"pages/repos/[owner]/[repo]/check-runs/[check_run_id]/annotations.js" = "//pages/repos/[owner]/[repo]/check-runs/[check_run_id]/annotations.js",
- .@"pages/repos/[owner]/[repo]/check-suites/[check_suite_id].js" = "//pages/repos/[owner]/[repo]/check-suites/[check_suite_id].js",
- .@"pages/repos/[owner]/[repo]/check-suites/[check_suite_id]/check-runs.js" = "//pages/repos/[owner]/[repo]/check-suites/[check_suite_id]/check-runs.js",
- .@"pages/repos/[owner]/[repo]/code-scanning/alerts.js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts.js",
- .@"pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number].js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number].js",
- .@"pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number]/instances.js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number]/instances.js",
- .@"pages/repos/[owner]/[repo]/code-scanning/analyses.js" = "//pages/repos/[owner]/[repo]/code-scanning/analyses.js",
- .@"pages/repos/[owner]/[repo]/code-scanning/analyses/[analysis_id].js" = "//pages/repos/[owner]/[repo]/code-scanning/analyses/[analysis_id].js",
- .@"pages/repos/[owner]/[repo]/code-scanning/sarifs/[sarif_id].js" = "//pages/repos/[owner]/[repo]/code-scanning/sarifs/[sarif_id].js",
- .@"pages/repos/[owner]/[repo]/collaborators.js" = "//pages/repos/[owner]/[repo]/collaborators.js",
- .@"pages/repos/[owner]/[repo]/collaborators/[username].js" = "//pages/repos/[owner]/[repo]/collaborators/[username].js",
- .@"pages/repos/[owner]/[repo]/collaborators/[username]/permission.js" = "//pages/repos/[owner]/[repo]/collaborators/[username]/permission.js",
- .@"pages/repos/[owner]/[repo]/comments.js" = "//pages/repos/[owner]/[repo]/comments.js",
- .@"pages/repos/[owner]/[repo]/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/comments/[comment_id].js",
- .@"pages/repos/[owner]/[repo]/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/comments/[comment_id]/reactions.js",
- .@"pages/repos/[owner]/[repo]/commits.js" = "//pages/repos/[owner]/[repo]/commits.js",
- .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/branches-where-head.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/branches-where-head.js",
- .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/comments.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/comments.js",
- .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/pulls.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/pulls.js",
- .@"pages/repos/[owner]/[repo]/commits/[ref].js" = "//pages/repos/[owner]/[repo]/commits/[ref].js",
- .@"pages/repos/[owner]/[repo]/commits/[ref]/check-runs.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/check-runs.js",
- .@"pages/repos/[owner]/[repo]/commits/[ref]/check-suites.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/check-suites.js",
- .@"pages/repos/[owner]/[repo]/commits/[ref]/status.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/status.js",
- .@"pages/repos/[owner]/[repo]/commits/[ref]/statuses.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/statuses.js",
- .@"pages/repos/[owner]/[repo]/community/profile.js" = "//pages/repos/[owner]/[repo]/community/profile.js",
- .@"pages/repos/[owner]/[repo]/compare/[basehead].js" = "//pages/repos/[owner]/[repo]/compare/[basehead].js",
- .@"pages/repos/[owner]/[repo]/contents/[path].js" = "//pages/repos/[owner]/[repo]/contents/[path].js",
- .@"pages/repos/[owner]/[repo]/contributors.js" = "//pages/repos/[owner]/[repo]/contributors.js",
- .@"pages/repos/[owner]/[repo]/deployments.js" = "//pages/repos/[owner]/[repo]/deployments.js",
- .@"pages/repos/[owner]/[repo]/deployments/[deployment_id].js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id].js",
- .@"pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses.js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses.js",
- .@"pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses/[status_id].js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses/[status_id].js",
- .@"pages/repos/[owner]/[repo]/environments.js" = "//pages/repos/[owner]/[repo]/environments.js",
- .@"pages/repos/[owner]/[repo]/environments/[environment_name].js" = "//pages/repos/[owner]/[repo]/environments/[environment_name].js",
- .@"pages/repos/[owner]/[repo]/events.js" = "//pages/repos/[owner]/[repo]/events.js",
- .@"pages/repos/[owner]/[repo]/forks.js" = "//pages/repos/[owner]/[repo]/forks.js",
- .@"pages/repos/[owner]/[repo]/git/blobs/[file_sha].js" = "//pages/repos/[owner]/[repo]/git/blobs/[file_sha].js",
- .@"pages/repos/[owner]/[repo]/git/commits/[commit_sha].js" = "//pages/repos/[owner]/[repo]/git/commits/[commit_sha].js",
- .@"pages/repos/[owner]/[repo]/git/matching-refs/[ref].js" = "//pages/repos/[owner]/[repo]/git/matching-refs/[ref].js",
- .@"pages/repos/[owner]/[repo]/git/ref/[ref].js" = "//pages/repos/[owner]/[repo]/git/ref/[ref].js",
- .@"pages/repos/[owner]/[repo]/git/tags/[tag_sha].js" = "//pages/repos/[owner]/[repo]/git/tags/[tag_sha].js",
- .@"pages/repos/[owner]/[repo]/git/trees/[tree_sha].js" = "//pages/repos/[owner]/[repo]/git/trees/[tree_sha].js",
- .@"pages/repos/[owner]/[repo]/hooks.js" = "//pages/repos/[owner]/[repo]/hooks.js",
- .@"pages/repos/[owner]/[repo]/hooks/[hook_id].js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id].js",
- .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/config.js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/config.js",
- .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries.js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries.js",
- .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries/[delivery_id].js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries/[delivery_id].js",
- .@"pages/repos/[owner]/[repo]/import.js" = "//pages/repos/[owner]/[repo]/import.js",
- .@"pages/repos/[owner]/[repo]/import/authors.js" = "//pages/repos/[owner]/[repo]/import/authors.js",
- .@"pages/repos/[owner]/[repo]/import/large_files.js" = "//pages/repos/[owner]/[repo]/import/large_files.js",
- .@"pages/repos/[owner]/[repo]/interaction-limits.js" = "//pages/repos/[owner]/[repo]/interaction-limits.js",
- .@"pages/repos/[owner]/[repo]/invitations.js" = "//pages/repos/[owner]/[repo]/invitations.js",
- .@"pages/repos/[owner]/[repo]/issues.js" = "//pages/repos/[owner]/[repo]/issues.js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number].js" = "//pages/repos/[owner]/[repo]/issues/[issue_number].js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number]/comments.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/comments.js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number]/events.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/events.js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number]/labels.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/labels.js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number]/reactions.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/reactions.js",
- .@"pages/repos/[owner]/[repo]/issues/[issue_number]/timeline.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/timeline.js",
- .@"pages/repos/[owner]/[repo]/issues/comments.js" = "//pages/repos/[owner]/[repo]/issues/comments.js",
- .@"pages/repos/[owner]/[repo]/issues/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/issues/comments/[comment_id].js",
- .@"pages/repos/[owner]/[repo]/issues/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/issues/comments/[comment_id]/reactions.js",
- .@"pages/repos/[owner]/[repo]/issues/events.js" = "//pages/repos/[owner]/[repo]/issues/events.js",
- .@"pages/repos/[owner]/[repo]/issues/events/[event_id].js" = "//pages/repos/[owner]/[repo]/issues/events/[event_id].js",
- .@"pages/repos/[owner]/[repo]/keys.js" = "//pages/repos/[owner]/[repo]/keys.js",
- .@"pages/repos/[owner]/[repo]/keys/[key_id].js" = "//pages/repos/[owner]/[repo]/keys/[key_id].js",
- .@"pages/repos/[owner]/[repo]/labels.js" = "//pages/repos/[owner]/[repo]/labels.js",
- .@"pages/repos/[owner]/[repo]/labels/[name].js" = "//pages/repos/[owner]/[repo]/labels/[name].js",
- .@"pages/repos/[owner]/[repo]/languages.js" = "//pages/repos/[owner]/[repo]/languages.js",
- .@"pages/repos/[owner]/[repo]/license.js" = "//pages/repos/[owner]/[repo]/license.js",
- .@"pages/repos/[owner]/[repo]/milestones.js" = "//pages/repos/[owner]/[repo]/milestones.js",
- .@"pages/repos/[owner]/[repo]/milestones/[milestone_number].js" = "//pages/repos/[owner]/[repo]/milestones/[milestone_number].js",
- .@"pages/repos/[owner]/[repo]/milestones/[milestone_number]/labels.js" = "//pages/repos/[owner]/[repo]/milestones/[milestone_number]/labels.js",
- .@"pages/repos/[owner]/[repo]/pages.js" = "//pages/repos/[owner]/[repo]/pages.js",
- .@"pages/repos/[owner]/[repo]/pages/builds.js" = "//pages/repos/[owner]/[repo]/pages/builds.js",
- .@"pages/repos/[owner]/[repo]/pages/builds/[build_id].js" = "//pages/repos/[owner]/[repo]/pages/builds/[build_id].js",
- .@"pages/repos/[owner]/[repo]/pages/builds/latest.js" = "//pages/repos/[owner]/[repo]/pages/builds/latest.js",
- .@"pages/repos/[owner]/[repo]/pages/health.js" = "//pages/repos/[owner]/[repo]/pages/health.js",
- .@"pages/repos/[owner]/[repo]/projects.js" = "//pages/repos/[owner]/[repo]/projects.js",
- .@"pages/repos/[owner]/[repo]/pulls.js" = "//pages/repos/[owner]/[repo]/pulls.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number].js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number].js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/comments.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/comments.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/commits.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/commits.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/files.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/files.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/merge.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/merge.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/requested_reviewers.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/requested_reviewers.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews.js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id].js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id].js",
- .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id]/comments.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id]/comments.js",
- .@"pages/repos/[owner]/[repo]/pulls/comments.js" = "//pages/repos/[owner]/[repo]/pulls/comments.js",
- .@"pages/repos/[owner]/[repo]/pulls/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/pulls/comments/[comment_id].js",
- .@"pages/repos/[owner]/[repo]/pulls/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/pulls/comments/[comment_id]/reactions.js",
- .@"pages/repos/[owner]/[repo]/readme.js" = "//pages/repos/[owner]/[repo]/readme.js",
- .@"pages/repos/[owner]/[repo]/readme/[dir].js" = "//pages/repos/[owner]/[repo]/readme/[dir].js",
- .@"pages/repos/[owner]/[repo]/releases.js" = "//pages/repos/[owner]/[repo]/releases.js",
- .@"pages/repos/[owner]/[repo]/releases/[release_id].js" = "//pages/repos/[owner]/[repo]/releases/[release_id].js",
- .@"pages/repos/[owner]/[repo]/releases/[release_id]/assets.js" = "//pages/repos/[owner]/[repo]/releases/[release_id]/assets.js",
- .@"pages/repos/[owner]/[repo]/releases/assets/[asset_id].js" = "//pages/repos/[owner]/[repo]/releases/assets/[asset_id].js",
- .@"pages/repos/[owner]/[repo]/releases/latest.js" = "//pages/repos/[owner]/[repo]/releases/latest.js",
- .@"pages/repos/[owner]/[repo]/releases/tags/[tag].js" = "//pages/repos/[owner]/[repo]/releases/tags/[tag].js",
- .@"pages/repos/[owner]/[repo]/secret-scanning/alerts.js" = "//pages/repos/[owner]/[repo]/secret-scanning/alerts.js",
- .@"pages/repos/[owner]/[repo]/secret-scanning/alerts/[alert_number].js" = "//pages/repos/[owner]/[repo]/secret-scanning/alerts/[alert_number].js",
- .@"pages/repos/[owner]/[repo]/stargazers.js" = "//pages/repos/[owner]/[repo]/stargazers.js",
- .@"pages/repos/[owner]/[repo]/stats/code_frequency.js" = "//pages/repos/[owner]/[repo]/stats/code_frequency.js",
- .@"pages/repos/[owner]/[repo]/stats/commit_activity.js" = "//pages/repos/[owner]/[repo]/stats/commit_activity.js",
- .@"pages/repos/[owner]/[repo]/stats/contributors.js" = "//pages/repos/[owner]/[repo]/stats/contributors.js",
- .@"pages/repos/[owner]/[repo]/stats/participation.js" = "//pages/repos/[owner]/[repo]/stats/participation.js",
- .@"pages/repos/[owner]/[repo]/stats/punch_card.js" = "//pages/repos/[owner]/[repo]/stats/punch_card.js",
- .@"pages/repos/[owner]/[repo]/subscribers.js" = "//pages/repos/[owner]/[repo]/subscribers.js",
- .@"pages/repos/[owner]/[repo]/tags.js" = "//pages/repos/[owner]/[repo]/tags.js",
- .@"pages/repos/[owner]/[repo]/tarball/[ref].js" = "//pages/repos/[owner]/[repo]/tarball/[ref].js",
- .@"pages/repos/[owner]/[repo]/teams.js" = "//pages/repos/[owner]/[repo]/teams.js",
- .@"pages/repos/[owner]/[repo]/topics.js" = "//pages/repos/[owner]/[repo]/topics.js",
- .@"pages/repos/[owner]/[repo]/traffic/clones.js" = "//pages/repos/[owner]/[repo]/traffic/clones.js",
- .@"pages/repos/[owner]/[repo]/traffic/popular/paths.js" = "//pages/repos/[owner]/[repo]/traffic/popular/paths.js",
- .@"pages/repos/[owner]/[repo]/traffic/popular/referrers.js" = "//pages/repos/[owner]/[repo]/traffic/popular/referrers.js",
- .@"pages/repos/[owner]/[repo]/traffic/views.js" = "//pages/repos/[owner]/[repo]/traffic/views.js",
- .@"pages/repos/[owner]/[repo]/zipball/[ref].js" = "//pages/repos/[owner]/[repo]/zipball/[ref].js",
- .@"pages/repositories.js" = "//pages/repositories.js",
- .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets.js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets.js",
- .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets/[secret_name].js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets/[secret_name].js",
- .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets/public-key.js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets/public-key.js",
- .@"pages/scim/v2/enterprises/[enterprise]/Groups.js" = "//pages/scim/v2/enterprises/[enterprise]/Groups.js",
- .@"pages/scim/v2/enterprises/[enterprise]/Groups/[scim_group_id].js" = "//pages/scim/v2/enterprises/[enterprise]/Groups/[scim_group_id].js",
- .@"pages/scim/v2/enterprises/[enterprise]/Users.js" = "//pages/scim/v2/enterprises/[enterprise]/Users.js",
- .@"pages/scim/v2/enterprises/[enterprise]/Users/[scim_user_id].js" = "//pages/scim/v2/enterprises/[enterprise]/Users/[scim_user_id].js",
- .@"pages/scim/v2/organizations/[org]/Users.js" = "//pages/scim/v2/organizations/[org]/Users.js",
- .@"pages/scim/v2/organizations/[org]/Users/[scim_user_id].js" = "//pages/scim/v2/organizations/[org]/Users/[scim_user_id].js",
- .@"pages/search/code.js" = "//pages/search/code.js",
- .@"pages/search/commits.js" = "//pages/search/commits.js",
- .@"pages/search/issues.js" = "//pages/search/issues.js",
- .@"pages/search/labels.js" = "//pages/search/labels.js",
- .@"pages/search/repositories.js" = "//pages/search/repositories.js",
- .@"pages/search/topics.js" = "//pages/search/topics.js",
- .@"pages/search/users.js" = "//pages/search/users.js",
- .@"pages/teams/[team_id].js" = "//pages/teams/[team_id].js",
- .@"pages/teams/[team_id]/discussions.js" = "//pages/teams/[team_id]/discussions.js",
- .@"pages/teams/[team_id]/discussions/[discussion_number].js" = "//pages/teams/[team_id]/discussions/[discussion_number].js",
- .@"pages/teams/[team_id]/discussions/[discussion_number]/comments.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments.js",
- .@"pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number].js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number].js",
- .@"pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number]/reactions.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number]/reactions.js",
- .@"pages/teams/[team_id]/discussions/[discussion_number]/reactions.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/reactions.js",
- .@"pages/teams/[team_id]/invitations.js" = "//pages/teams/[team_id]/invitations.js",
- .@"pages/teams/[team_id]/members.js" = "//pages/teams/[team_id]/members.js",
- .@"pages/teams/[team_id]/members/[username].js" = "//pages/teams/[team_id]/members/[username].js",
- .@"pages/teams/[team_id]/memberships/[username].js" = "//pages/teams/[team_id]/memberships/[username].js",
- .@"pages/teams/[team_id]/projects.js" = "//pages/teams/[team_id]/projects.js",
- .@"pages/teams/[team_id]/projects/[project_id].js" = "//pages/teams/[team_id]/projects/[project_id].js",
- .@"pages/teams/[team_id]/repos.js" = "//pages/teams/[team_id]/repos.js",
- .@"pages/teams/[team_id]/repos/[owner]/[repo].js" = "//pages/teams/[team_id]/repos/[owner]/[repo].js",
- .@"pages/teams/[team_id]/teams.js" = "//pages/teams/[team_id]/teams.js",
- .@"pages/users.js" = "//pages/users.js",
- .@"pages/users/[username].js" = "//pages/users/[username].js",
- .@"pages/users/[username]/events.js" = "//pages/users/[username]/events.js",
- .@"pages/users/[username]/events/public.js" = "//pages/users/[username]/events/public.js",
- .@"pages/users/[username]/followers.js" = "//pages/users/[username]/followers.js",
- .@"pages/users/[username]/following.js" = "//pages/users/[username]/following.js",
- .@"pages/users/[username]/following/[target_user].js" = "//pages/users/[username]/following/[target_user].js",
- .@"pages/users/[username]/gpg_keys.js" = "//pages/users/[username]/gpg_keys.js",
- .@"pages/users/[username]/keys.js" = "//pages/users/[username]/keys.js",
- .@"pages/users/[username]/orgs.js" = "//pages/users/[username]/orgs.js",
- .@"pages/users/[username]/received_events.js" = "//pages/users/[username]/received_events.js",
- .@"pages/users/[username]/received_events/public.js" = "//pages/users/[username]/received_events/public.js",
- .@"pages/users/[username]/repos.js" = "//pages/users/[username]/repos.js",
- .@"pages/users/[username]/starred.js" = "//pages/users/[username]/starred.js",
- .@"pages/users/[username]/subscriptions.js" = "//pages/users/[username]/subscriptions.js",
- .@"pages/zen.js" = "//pages/zen.js",
-};
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index dc786c0b3..94fc29b09 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -718,43 +718,30 @@ pub fn utf8ByteSequenceLength(first_byte: u8) u3 {
pub fn NewCodePointIterator(comptime CodePointType: type, comptime zeroValue: comptime_int) type {
return struct {
const Iterator = @This();
- bytes: [*]const u8,
- i: u32 = 0,
- len: u32 = 0,
+ bytes: []const u8,
+ i: usize,
width: u3 = 0,
- c: CodePointType = zeroValue,
+ c: CodePointType = 0,
- pub fn initOffset(bytes: []const u8, offset: u32) Iterator {
- return Iterator{
- .bytes = bytes.ptr,
- .i = offset,
- .len = @truncate(u32, bytes.len),
- };
- }
-
- pub inline fn isEnd(this: Iterator) bool {
- return this.c == zeroValue and @minimum(this.len, this.i) >= this.len;
+ pub fn init(str: string) CodepointIterator {
+ return CodepointIterator{ .bytes = str, .i = 0, .width = 0, .c = 0 };
}
- pub fn init(bytes: []const u8) Iterator {
- return Iterator{
- .bytes = bytes.ptr,
- .i = 0,
- .len = @truncate(u32, bytes.len),
- };
+ pub fn initOffset(str: string, i: usize) CodepointIterator {
+ return CodepointIterator{ .bytes = str, .i = i, .width = 0, .c = 0 };
}
inline fn nextCodepointSlice(it: *Iterator) []const u8 {
@setRuntimeSafety(false);
const cp_len = utf8ByteSequenceLength(it.bytes[it.i]);
- it.i = @minimum(it.i + cp_len, it.len);
+ it.i += cp_len;
- return if (!(it.i + 1 > it.len)) it.bytes[it.i - cp_len .. it.i] else "";
+ return if (!(it.i > it.bytes.len)) it.bytes[it.i - cp_len .. it.i] else "";
}
pub fn needsUTF8Decoding(slice: string) bool {
- var it = Iterator.init(slice);
+ var it = Iterator{ .bytes = slice, .i = 0 };
while (true) {
const part = it.nextCodepointSlice();
@@ -821,10 +808,6 @@ pub fn NewCodePointIterator(comptime CodePointType: type, comptime zeroValue: co
};
}
- pub fn remaining(it: *Iterator) []const u8 {
- return it.bytes[it.i..it.len];
- }
-
/// Look ahead at the next n codepoints without advancing the iterator.
/// If fewer than n codepoints are available, then return the remainder of the string.
pub fn peek(it: *Iterator, n: usize) []const u8 {
@@ -834,7 +817,7 @@ pub fn NewCodePointIterator(comptime CodePointType: type, comptime zeroValue: co
var end_ix = original_i;
var found: usize = 0;
while (found < n) : (found += 1) {
- const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..it.len];
+ const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..];
end_ix += next_codepoint.len;
}
diff --git a/src/test/fixtures.zig b/src/test/fixtures.zig
index 8733a4e68..2c2f0d992 100644
--- a/src/test/fixtures.zig
+++ b/src/test/fixtures.zig
@@ -7,3 +7,557 @@ pub const fixtures = std.ComptimeStringMap([]u8, .{
.{ "simple-component.tsx", @embedFile("./fixtures/simple-component.tsx") },
.{ "simple-component.tsx", @embedFile("./fixtures/simple-component.tsx") },
});
+
+pub const github_api_routes_list = .{
+ .@"pages/[...catch-all-at-root].js" = "//pages/[...catch-all-at-root].js",
+ .@"pages/index.js" = "//pages/index.js",
+ .@"pages/app.js" = "//pages/app.js",
+ .@"pages/app/installations.js" = "//pages/app/installations.js",
+ .@"pages/app/installations/[installation_id].js" = "//pages/app/installations/[installation_id].js",
+ .@"pages/apps/[app_slug].js" = "//pages/apps/[app_slug].js",
+ .@"pages/codes_of_conduct.js" = "//pages/codes_of_conduct.js",
+ .@"pages/codes_of_conduct/[key].js" = "//pages/codes_of_conduct/[key].js",
+ .@"pages/emojis.js" = "//pages/emojis.js",
+ .@"pages/events.js" = "//pages/events.js",
+ .@"pages/feeds.js" = "//pages/feeds.js",
+ .@"pages/gitignore/templates.js" = "//pages/gitignore/templates.js",
+ .@"pages/gitignore/templates/[name].js" = "//pages/gitignore/templates/[name].js",
+ .@"pages/installation/repositories.js" = "//pages/installation/repositories.js",
+ .@"pages/licenses.js" = "//pages/licenses.js",
+ .@"pages/licenses/[license].js" = "//pages/licenses/[license].js",
+ .@"pages/meta.js" = "//pages/meta.js",
+ .@"pages/networks/[owner]/[repo]/events.js" = "//pages/networks/[owner]/[repo]/events.js",
+ .@"pages/octocat.js" = "//pages/octocat.js",
+ .@"pages/organizations.js" = "//pages/organizations.js",
+ .@"pages/orgs/[org]/index.js" = "//pages/orgs/[org].js",
+ .@"pages/orgs/[org]/actions/permissions.js" = "//pages/orgs/[org]/actions/permissions.js",
+ .@"pages/orgs/[org]/actions/permissions/repositories.js" = "//pages/orgs/[org]/actions/permissions/repositories.js",
+ .@"pages/orgs/[org]/actions/permissions/selected-actions.js" = "//pages/orgs/[org]/actions/permissions/selected-actions.js",
+ .@"pages/orgs/[org]/actions/runner-groups.js" = "//pages/orgs/[org]/actions/runner-groups.js",
+ .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id].js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id].js",
+ .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id]/repositories.js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id]/repositories.js",
+ .@"pages/orgs/[org]/actions/runner-groups/[runner_group_id]/runners.js" = "//pages/orgs/[org]/actions/runner-groups/[runner_group_id]/runners.js",
+ .@"pages/orgs/[org]/actions/runners.js" = "//pages/orgs/[org]/actions/runners.js",
+ .@"pages/orgs/[org]/actions/runners/[runner_id].js" = "//pages/orgs/[org]/actions/runners/[runner_id].js",
+ .@"pages/orgs/[org]/actions/runners/downloads.js" = "//pages/orgs/[org]/actions/runners/downloads.js",
+ .@"pages/orgs/[org]/actions/secrets.js" = "//pages/orgs/[org]/actions/secrets.js",
+ .@"pages/orgs/[org]/actions/secrets/[secret_name].js" = "//pages/orgs/[org]/actions/secrets/[secret_name].js",
+ .@"pages/orgs/[org]/actions/secrets/[secret_name]/repositories.js" = "//pages/orgs/[org]/actions/secrets/[secret_name]/repositories.js",
+ .@"pages/orgs/[org]/actions/secrets/public-key.js" = "//pages/orgs/[org]/actions/secrets/public-key.js",
+ .@"pages/orgs/[org]/audit-log.js" = "//pages/orgs/[org]/audit-log.js",
+ .@"pages/orgs/[org]/blocks.js" = "//pages/orgs/[org]/blocks.js",
+ .@"pages/orgs/[org]/blocks/[username].js" = "//pages/orgs/[org]/blocks/[username].js",
+ .@"pages/orgs/[org]/credential-authorizations.js" = "//pages/orgs/[org]/credential-authorizations.js",
+ .@"pages/orgs/[org]/events.js" = "//pages/orgs/[org]/events.js",
+ .@"pages/orgs/[org]/external-group/[group_id].js" = "//pages/orgs/[org]/external-group/[group_id].js",
+ .@"pages/orgs/[org]/external-groups.js" = "//pages/orgs/[org]/external-groups.js",
+ .@"pages/orgs/[org]/failed_invitations.js" = "//pages/orgs/[org]/failed_invitations.js",
+ .@"pages/orgs/[org]/hooks.js" = "//pages/orgs/[org]/hooks.js",
+ .@"pages/orgs/[org]/hooks/[hook_id].js" = "//pages/orgs/[org]/hooks/[hook_id].js",
+ .@"pages/orgs/[org]/hooks/[hook_id]/config.js" = "//pages/orgs/[org]/hooks/[hook_id]/config.js",
+ .@"pages/orgs/[org]/hooks/[hook_id]/deliveries.js" = "//pages/orgs/[org]/hooks/[hook_id]/deliveries.js",
+ .@"pages/orgs/[org]/hooks/[hook_id]/deliveries/[delivery_id].js" = "//pages/orgs/[org]/hooks/[hook_id]/deliveries/[delivery_id].js",
+ .@"pages/orgs/[org]/installations.js" = "//pages/orgs/[org]/installations.js",
+ .@"pages/orgs/[org]/interaction-limits.js" = "//pages/orgs/[org]/interaction-limits.js",
+ .@"pages/orgs/[org]/invitations.js" = "//pages/orgs/[org]/invitations.js",
+ .@"pages/orgs/[org]/invitations/[invitation_id]/teams.js" = "//pages/orgs/[org]/invitations/[invitation_id]/teams.js",
+ .@"pages/orgs/[org]/members.js" = "//pages/orgs/[org]/members.js",
+ .@"pages/orgs/[org]/members/[username].js" = "//pages/orgs/[org]/members/[username].js",
+ .@"pages/orgs/[org]/memberships/[username].js" = "//pages/orgs/[org]/memberships/[username].js",
+ .@"pages/orgs/[org]/outside_collaborators.js" = "//pages/orgs/[org]/outside_collaborators.js",
+ .@"pages/orgs/[org]/projects.js" = "//pages/orgs/[org]/projects.js",
+ .@"pages/orgs/[org]/public_members.js" = "//pages/orgs/[org]/public_members.js",
+ .@"pages/orgs/[org]/public_members/[username].js" = "//pages/orgs/[org]/public_members/[username].js",
+ .@"pages/orgs/[org]/repos.js" = "//pages/orgs/[org]/repos.js",
+ .@"pages/orgs/[org]/secret-scanning/alerts.js" = "//pages/orgs/[org]/secret-scanning/alerts.js",
+ .@"pages/orgs/[org]/team-sync/groups.js" = "//pages/orgs/[org]/team-sync/groups.js",
+ .@"pages/orgs/[org]/teams.js" = "//pages/orgs/[org]/teams.js",
+ .@"pages/orgs/[org]/teams/[team_slug].js" = "//pages/orgs/[org]/teams/[team_slug].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number].js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number].js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number]/reactions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/comments/[comment_number]/reactions.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/reactions.js" = "//pages/orgs/[org]/teams/[team_slug]/discussions/[discussion_number]/reactions.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/invitations.js" = "//pages/orgs/[org]/teams/[team_slug]/invitations.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/members.js" = "//pages/orgs/[org]/teams/[team_slug]/members.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/memberships/[username].js" = "//pages/orgs/[org]/teams/[team_slug]/memberships/[username].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/projects.js" = "//pages/orgs/[org]/teams/[team_slug]/projects.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/projects/[project_id].js" = "//pages/orgs/[org]/teams/[team_slug]/projects/[project_id].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/repos.js" = "//pages/orgs/[org]/teams/[team_slug]/repos.js",
+ .@"pages/orgs/[org]/teams/[team_slug]/repos/[owner]/[repo].js" = "//pages/orgs/[org]/teams/[team_slug]/repos/[owner]/[repo].js",
+ .@"pages/orgs/[org]/teams/[team_slug]/teams.js" = "//pages/orgs/[org]/teams/[team_slug]/teams.js",
+ .@"pages/projects/[project_id].js" = "//pages/projects/[project_id].js",
+ .@"pages/projects/[project_id]/collaborators.js" = "//pages/projects/[project_id]/collaborators.js",
+ .@"pages/projects/[project_id]/collaborators/[username]/permission.js" = "//pages/projects/[project_id]/collaborators/[username]/permission.js",
+ .@"pages/projects/[project_id]/columns.js" = "//pages/projects/[project_id]/columns.js",
+ .@"pages/projects/columns/[column_id].js" = "//pages/projects/columns/[column_id].js",
+ .@"pages/projects/columns/[column_id]/cards.js" = "//pages/projects/columns/[column_id]/cards.js",
+ .@"pages/projects/columns/cards/[card_id].js" = "//pages/projects/columns/cards/[card_id].js",
+ .@"pages/rate_limit.js" = "//pages/rate_limit.js",
+ .@"pages/repos/[owner]/[repo].js" = "//pages/repos/[owner]/[repo].js",
+ .@"pages/repos/[owner]/[repo]/actions/artifacts.js" = "//pages/repos/[owner]/[repo]/actions/artifacts.js",
+ .@"pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id].js" = "//pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id].js",
+ .@"pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id]/[archive_format].js" = "//pages/repos/[owner]/[repo]/actions/artifacts/[artifact_id]/[archive_format].js",
+ .@"pages/repos/[owner]/[repo]/actions/jobs/[job_id].js" = "//pages/repos/[owner]/[repo]/actions/jobs/[job_id].js",
+ .@"pages/repos/[owner]/[repo]/actions/jobs/[job_id]/logs.js" = "//pages/repos/[owner]/[repo]/actions/jobs/[job_id]/logs.js",
+ .@"pages/repos/[owner]/[repo]/actions/permissions.js" = "//pages/repos/[owner]/[repo]/actions/permissions.js",
+ .@"pages/repos/[owner]/[repo]/actions/permissions/selected-actions.js" = "//pages/repos/[owner]/[repo]/actions/permissions/selected-actions.js",
+ .@"pages/repos/[owner]/[repo]/actions/runners.js" = "//pages/repos/[owner]/[repo]/actions/runners.js",
+ .@"pages/repos/[owner]/[repo]/actions/runners/[runner_id].js" = "//pages/repos/[owner]/[repo]/actions/runners/[runner_id].js",
+ .@"pages/repos/[owner]/[repo]/actions/runners/downloads.js" = "//pages/repos/[owner]/[repo]/actions/runners/downloads.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs.js" = "//pages/repos/[owner]/[repo]/actions/runs.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id].js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id].js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/approvals.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/approvals.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/artifacts.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/artifacts.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number].js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number].js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/jobs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/jobs.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/logs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/attempts/[attempt_number]/logs.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/jobs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/jobs.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/logs.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/logs.js",
+ .@"pages/repos/[owner]/[repo]/actions/runs/[run_id]/pending_deployments.js" = "//pages/repos/[owner]/[repo]/actions/runs/[run_id]/pending_deployments.js",
+ .@"pages/repos/[owner]/[repo]/actions/secrets.js" = "//pages/repos/[owner]/[repo]/actions/secrets.js",
+ .@"pages/repos/[owner]/[repo]/actions/secrets/[secret_name].js" = "//pages/repos/[owner]/[repo]/actions/secrets/[secret_name].js",
+ .@"pages/repos/[owner]/[repo]/actions/secrets/public-key.js" = "//pages/repos/[owner]/[repo]/actions/secrets/public-key.js",
+ .@"pages/repos/[owner]/[repo]/actions/workflows.js" = "//pages/repos/[owner]/[repo]/actions/workflows.js",
+ .@"pages/repos/[owner]/[repo]/actions/workflows/[workflow_id].js" = "//pages/repos/[owner]/[repo]/actions/workflows/[workflow_id].js",
+ .@"pages/repos/[owner]/[repo]/actions/workflows/[workflow_id]/runs.js" = "//pages/repos/[owner]/[repo]/actions/workflows/[workflow_id]/runs.js",
+ .@"pages/repos/[owner]/[repo]/assignees.js" = "//pages/repos/[owner]/[repo]/assignees.js",
+ .@"pages/repos/[owner]/[repo]/assignees/[assignee].js" = "//pages/repos/[owner]/[repo]/assignees/[assignee].js",
+ .@"pages/repos/[owner]/[repo]/autolinks.js" = "//pages/repos/[owner]/[repo]/autolinks.js",
+ .@"pages/repos/[owner]/[repo]/autolinks/[autolink_id].js" = "//pages/repos/[owner]/[repo]/autolinks/[autolink_id].js",
+ .@"pages/repos/[owner]/[repo]/branches.js" = "//pages/repos/[owner]/[repo]/branches.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch].js" = "//pages/repos/[owner]/[repo]/branches/[branch].js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/enforce_admins.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/enforce_admins.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_pull_request_reviews.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_pull_request_reviews.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_signatures.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_signatures.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks/contexts.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/required_status_checks/contexts.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/apps.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/apps.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/teams.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/teams.js",
+ .@"pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/users.js" = "//pages/repos/[owner]/[repo]/branches/[branch]/protection/restrictions/users.js",
+ .@"pages/repos/[owner]/[repo]/check-runs/[check_run_id].js" = "//pages/repos/[owner]/[repo]/check-runs/[check_run_id].js",
+ .@"pages/repos/[owner]/[repo]/check-runs/[check_run_id]/annotations.js" = "//pages/repos/[owner]/[repo]/check-runs/[check_run_id]/annotations.js",
+ .@"pages/repos/[owner]/[repo]/check-suites/[check_suite_id].js" = "//pages/repos/[owner]/[repo]/check-suites/[check_suite_id].js",
+ .@"pages/repos/[owner]/[repo]/check-suites/[check_suite_id]/check-runs.js" = "//pages/repos/[owner]/[repo]/check-suites/[check_suite_id]/check-runs.js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/alerts.js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts.js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number].js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number].js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number]/instances.js" = "//pages/repos/[owner]/[repo]/code-scanning/alerts/[alert_number]/instances.js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/analyses.js" = "//pages/repos/[owner]/[repo]/code-scanning/analyses.js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/analyses/[analysis_id].js" = "//pages/repos/[owner]/[repo]/code-scanning/analyses/[analysis_id].js",
+ .@"pages/repos/[owner]/[repo]/code-scanning/sarifs/[sarif_id].js" = "//pages/repos/[owner]/[repo]/code-scanning/sarifs/[sarif_id].js",
+ .@"pages/repos/[owner]/[repo]/collaborators.js" = "//pages/repos/[owner]/[repo]/collaborators.js",
+ .@"pages/repos/[owner]/[repo]/collaborators/[username].js" = "//pages/repos/[owner]/[repo]/collaborators/[username].js",
+ .@"pages/repos/[owner]/[repo]/collaborators/[username]/permission.js" = "//pages/repos/[owner]/[repo]/collaborators/[username]/permission.js",
+ .@"pages/repos/[owner]/[repo]/comments.js" = "//pages/repos/[owner]/[repo]/comments.js",
+ .@"pages/repos/[owner]/[repo]/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/comments/[comment_id].js",
+ .@"pages/repos/[owner]/[repo]/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/comments/[comment_id]/reactions.js",
+ .@"pages/repos/[owner]/[repo]/commits.js" = "//pages/repos/[owner]/[repo]/commits.js",
+ .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/branches-where-head.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/branches-where-head.js",
+ .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/comments.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/comments.js",
+ .@"pages/repos/[owner]/[repo]/commits/[commit_sha]/pulls.js" = "//pages/repos/[owner]/[repo]/commits/[commit_sha]/pulls.js",
+ .@"pages/repos/[owner]/[repo]/commits/[ref].js" = "//pages/repos/[owner]/[repo]/commits/[ref].js",
+ .@"pages/repos/[owner]/[repo]/commits/[ref]/check-runs.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/check-runs.js",
+ .@"pages/repos/[owner]/[repo]/commits/[ref]/check-suites.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/check-suites.js",
+ .@"pages/repos/[owner]/[repo]/commits/[ref]/status.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/status.js",
+ .@"pages/repos/[owner]/[repo]/commits/[ref]/statuses.js" = "//pages/repos/[owner]/[repo]/commits/[ref]/statuses.js",
+ .@"pages/repos/[owner]/[repo]/community/profile.js" = "//pages/repos/[owner]/[repo]/community/profile.js",
+ .@"pages/repos/[owner]/[repo]/compare/[basehead].js" = "//pages/repos/[owner]/[repo]/compare/[basehead].js",
+ .@"pages/repos/[owner]/[repo]/contents/[path].js" = "//pages/repos/[owner]/[repo]/contents/[path].js",
+ .@"pages/repos/[owner]/[repo]/contributors.js" = "//pages/repos/[owner]/[repo]/contributors.js",
+ .@"pages/repos/[owner]/[repo]/deployments.js" = "//pages/repos/[owner]/[repo]/deployments.js",
+ .@"pages/repos/[owner]/[repo]/deployments/[deployment_id].js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id].js",
+ .@"pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses.js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses.js",
+ .@"pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses/[status_id].js" = "//pages/repos/[owner]/[repo]/deployments/[deployment_id]/statuses/[status_id].js",
+ .@"pages/repos/[owner]/[repo]/environments.js" = "//pages/repos/[owner]/[repo]/environments.js",
+ .@"pages/repos/[owner]/[repo]/environments/[environment_name].js" = "//pages/repos/[owner]/[repo]/environments/[environment_name].js",
+ .@"pages/repos/[owner]/[repo]/events.js" = "//pages/repos/[owner]/[repo]/events.js",
+ .@"pages/repos/[owner]/[repo]/forks.js" = "//pages/repos/[owner]/[repo]/forks.js",
+ .@"pages/repos/[owner]/[repo]/git/blobs/[file_sha].js" = "//pages/repos/[owner]/[repo]/git/blobs/[file_sha].js",
+ .@"pages/repos/[owner]/[repo]/git/commits/[commit_sha].js" = "//pages/repos/[owner]/[repo]/git/commits/[commit_sha].js",
+ .@"pages/repos/[owner]/[repo]/git/matching-refs/[ref].js" = "//pages/repos/[owner]/[repo]/git/matching-refs/[ref].js",
+ .@"pages/repos/[owner]/[repo]/git/ref/[ref].js" = "//pages/repos/[owner]/[repo]/git/ref/[ref].js",
+ .@"pages/repos/[owner]/[repo]/git/tags/[tag_sha].js" = "//pages/repos/[owner]/[repo]/git/tags/[tag_sha].js",
+ .@"pages/repos/[owner]/[repo]/git/trees/[tree_sha].js" = "//pages/repos/[owner]/[repo]/git/trees/[tree_sha].js",
+ .@"pages/repos/[owner]/[repo]/hooks.js" = "//pages/repos/[owner]/[repo]/hooks.js",
+ .@"pages/repos/[owner]/[repo]/hooks/[hook_id].js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id].js",
+ .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/config.js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/config.js",
+ .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries.js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries.js",
+ .@"pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries/[delivery_id].js" = "//pages/repos/[owner]/[repo]/hooks/[hook_id]/deliveries/[delivery_id].js",
+ .@"pages/repos/[owner]/[repo]/import.js" = "//pages/repos/[owner]/[repo]/import.js",
+ .@"pages/repos/[owner]/[repo]/import/authors.js" = "//pages/repos/[owner]/[repo]/import/authors.js",
+ .@"pages/repos/[owner]/[repo]/import/large_files.js" = "//pages/repos/[owner]/[repo]/import/large_files.js",
+ .@"pages/repos/[owner]/[repo]/interaction-limits.js" = "//pages/repos/[owner]/[repo]/interaction-limits.js",
+ .@"pages/repos/[owner]/[repo]/invitations.js" = "//pages/repos/[owner]/[repo]/invitations.js",
+ .@"pages/repos/[owner]/[repo]/issues.js" = "//pages/repos/[owner]/[repo]/issues.js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number].js" = "//pages/repos/[owner]/[repo]/issues/[issue_number].js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number]/comments.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/comments.js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number]/events.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/events.js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number]/labels.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/labels.js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number]/reactions.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/reactions.js",
+ .@"pages/repos/[owner]/[repo]/issues/[issue_number]/timeline.js" = "//pages/repos/[owner]/[repo]/issues/[issue_number]/timeline.js",
+ .@"pages/repos/[owner]/[repo]/issues/comments.js" = "//pages/repos/[owner]/[repo]/issues/comments.js",
+ .@"pages/repos/[owner]/[repo]/issues/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/issues/comments/[comment_id].js",
+ .@"pages/repos/[owner]/[repo]/issues/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/issues/comments/[comment_id]/reactions.js",
+ .@"pages/repos/[owner]/[repo]/issues/events.js" = "//pages/repos/[owner]/[repo]/issues/events.js",
+ .@"pages/repos/[owner]/[repo]/issues/events/[event_id].js" = "//pages/repos/[owner]/[repo]/issues/events/[event_id].js",
+ .@"pages/repos/[owner]/[repo]/keys.js" = "//pages/repos/[owner]/[repo]/keys.js",
+ .@"pages/repos/[owner]/[repo]/keys/[key_id].js" = "//pages/repos/[owner]/[repo]/keys/[key_id].js",
+ .@"pages/repos/[owner]/[repo]/labels.js" = "//pages/repos/[owner]/[repo]/labels.js",
+ .@"pages/repos/[owner]/[repo]/labels/[name].js" = "//pages/repos/[owner]/[repo]/labels/[name].js",
+ .@"pages/repos/[owner]/[repo]/languages.js" = "//pages/repos/[owner]/[repo]/languages.js",
+ .@"pages/repos/[owner]/[repo]/license.js" = "//pages/repos/[owner]/[repo]/license.js",
+ .@"pages/repos/[owner]/[repo]/milestones.js" = "//pages/repos/[owner]/[repo]/milestones.js",
+ .@"pages/repos/[owner]/[repo]/milestones/[milestone_number].js" = "//pages/repos/[owner]/[repo]/milestones/[milestone_number].js",
+ .@"pages/repos/[owner]/[repo]/milestones/[milestone_number]/labels.js" = "//pages/repos/[owner]/[repo]/milestones/[milestone_number]/labels.js",
+ .@"pages/repos/[owner]/[repo]/pages.js" = "//pages/repos/[owner]/[repo]/pages.js",
+ .@"pages/repos/[owner]/[repo]/pages/builds.js" = "//pages/repos/[owner]/[repo]/pages/builds.js",
+ .@"pages/repos/[owner]/[repo]/pages/builds/[build_id].js" = "//pages/repos/[owner]/[repo]/pages/builds/[build_id].js",
+ .@"pages/repos/[owner]/[repo]/pages/builds/latest.js" = "//pages/repos/[owner]/[repo]/pages/builds/latest.js",
+ .@"pages/repos/[owner]/[repo]/pages/health.js" = "//pages/repos/[owner]/[repo]/pages/health.js",
+ .@"pages/repos/[owner]/[repo]/projects.js" = "//pages/repos/[owner]/[repo]/projects.js",
+ .@"pages/repos/[owner]/[repo]/pulls.js" = "//pages/repos/[owner]/[repo]/pulls.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number].js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number].js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/comments.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/comments.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/commits.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/commits.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/files.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/files.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/merge.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/merge.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/requested_reviewers.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/requested_reviewers.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews.js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id].js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id].js",
+ .@"pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id]/comments.js" = "//pages/repos/[owner]/[repo]/pulls/[pull_number]/reviews/[review_id]/comments.js",
+ .@"pages/repos/[owner]/[repo]/pulls/comments.js" = "//pages/repos/[owner]/[repo]/pulls/comments.js",
+ .@"pages/repos/[owner]/[repo]/pulls/comments/[comment_id].js" = "//pages/repos/[owner]/[repo]/pulls/comments/[comment_id].js",
+ .@"pages/repos/[owner]/[repo]/pulls/comments/[comment_id]/reactions.js" = "//pages/repos/[owner]/[repo]/pulls/comments/[comment_id]/reactions.js",
+ .@"pages/repos/[owner]/[repo]/readme.js" = "//pages/repos/[owner]/[repo]/readme.js",
+ .@"pages/repos/[owner]/[repo]/readme/[dir].js" = "//pages/repos/[owner]/[repo]/readme/[dir].js",
+ .@"pages/repos/[owner]/[repo]/releases.js" = "//pages/repos/[owner]/[repo]/releases.js",
+ .@"pages/repos/[owner]/[repo]/releases/[release_id].js" = "//pages/repos/[owner]/[repo]/releases/[release_id].js",
+ .@"pages/repos/[owner]/[repo]/releases/[release_id]/assets.js" = "//pages/repos/[owner]/[repo]/releases/[release_id]/assets.js",
+ .@"pages/repos/[owner]/[repo]/releases/assets/[asset_id].js" = "//pages/repos/[owner]/[repo]/releases/assets/[asset_id].js",
+ .@"pages/repos/[owner]/[repo]/releases/latest.js" = "//pages/repos/[owner]/[repo]/releases/latest.js",
+ .@"pages/repos/[owner]/[repo]/releases/tags/[tag].js" = "//pages/repos/[owner]/[repo]/releases/tags/[tag].js",
+ .@"pages/repos/[owner]/[repo]/secret-scanning/alerts.js" = "//pages/repos/[owner]/[repo]/secret-scanning/alerts.js",
+ .@"pages/repos/[owner]/[repo]/secret-scanning/alerts/[alert_number].js" = "//pages/repos/[owner]/[repo]/secret-scanning/alerts/[alert_number].js",
+ .@"pages/repos/[owner]/[repo]/stargazers.js" = "//pages/repos/[owner]/[repo]/stargazers.js",
+ .@"pages/repos/[owner]/[repo]/stats/code_frequency.js" = "//pages/repos/[owner]/[repo]/stats/code_frequency.js",
+ .@"pages/repos/[owner]/[repo]/stats/commit_activity.js" = "//pages/repos/[owner]/[repo]/stats/commit_activity.js",
+ .@"pages/repos/[owner]/[repo]/stats/contributors.js" = "//pages/repos/[owner]/[repo]/stats/contributors.js",
+ .@"pages/repos/[owner]/[repo]/stats/participation.js" = "//pages/repos/[owner]/[repo]/stats/participation.js",
+ .@"pages/repos/[owner]/[repo]/stats/punch_card.js" = "//pages/repos/[owner]/[repo]/stats/punch_card.js",
+ .@"pages/repos/[owner]/[repo]/subscribers.js" = "//pages/repos/[owner]/[repo]/subscribers.js",
+ .@"pages/repos/[owner]/[repo]/tags.js" = "//pages/repos/[owner]/[repo]/tags.js",
+ .@"pages/repos/[owner]/[repo]/tarball/[ref].js" = "//pages/repos/[owner]/[repo]/tarball/[ref].js",
+ .@"pages/repos/[owner]/[repo]/teams.js" = "//pages/repos/[owner]/[repo]/teams.js",
+ .@"pages/repos/[owner]/[repo]/topics.js" = "//pages/repos/[owner]/[repo]/topics.js",
+ .@"pages/repos/[owner]/[repo]/traffic/clones.js" = "//pages/repos/[owner]/[repo]/traffic/clones.js",
+ .@"pages/repos/[owner]/[repo]/traffic/popular/paths.js" = "//pages/repos/[owner]/[repo]/traffic/popular/paths.js",
+ .@"pages/repos/[owner]/[repo]/traffic/popular/referrers.js" = "//pages/repos/[owner]/[repo]/traffic/popular/referrers.js",
+ .@"pages/repos/[owner]/[repo]/traffic/views.js" = "//pages/repos/[owner]/[repo]/traffic/views.js",
+ .@"pages/repos/[owner]/[repo]/zipball/[ref].js" = "//pages/repos/[owner]/[repo]/zipball/[ref].js",
+ .@"pages/repositories.js" = "//pages/repositories.js",
+ .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets.js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets.js",
+ .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets/[secret_name].js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets/[secret_name].js",
+ .@"pages/repositories/[repository_id]/environments/[environment_name]/secrets/public-key.js" = "//pages/repositories/[repository_id]/environments/[environment_name]/secrets/public-key.js",
+ .@"pages/repositories/[repository_id]/[...jarred-fake-catch-all].js" = "//pages/repositories/[...jarred-fake-catch-all].js",
+ .@"pages/scim/v2/enterprises/[enterprise]/Groups.js" = "//pages/scim/v2/enterprises/[enterprise]/Groups.js",
+ .@"pages/scim/v2/enterprises/[enterprise]/Groups/[scim_group_id].js" = "//pages/scim/v2/enterprises/[enterprise]/Groups/[scim_group_id].js",
+ .@"pages/scim/v2/enterprises/[enterprise]/Users.js" = "//pages/scim/v2/enterprises/[enterprise]/Users.js",
+ .@"pages/scim/v2/enterprises/[enterprise]/Users/[scim_user_id].js" = "//pages/scim/v2/enterprises/[enterprise]/Users/[scim_user_id].js",
+ .@"pages/scim/v2/organizations/[org]/Users.js" = "//pages/scim/v2/organizations/[org]/Users.js",
+ .@"pages/scim/v2/organizations/[org]/Users/[scim_user_id].js" = "//pages/scim/v2/organizations/[org]/Users/[scim_user_id].js",
+ .@"pages/search/code.js" = "//pages/search/code.js",
+ .@"pages/search/commits.js" = "//pages/search/commits.js",
+ .@"pages/search/issues.js" = "//pages/search/issues.js",
+ .@"pages/search/labels.js" = "//pages/search/labels.js",
+ .@"pages/search/repositories.js" = "//pages/search/repositories.js",
+ .@"pages/search/topics.js" = "//pages/search/topics.js",
+ .@"pages/search/users.js" = "//pages/search/users.js",
+ .@"pages/teams/[team_id].js" = "//pages/teams/[team_id].js",
+ .@"pages/teams/[team_id]/discussions.js" = "//pages/teams/[team_id]/discussions.js",
+ .@"pages/teams/[team_id]/discussions/[discussion_number].js" = "//pages/teams/[team_id]/discussions/[discussion_number].js",
+ .@"pages/teams/[team_id]/discussions/[discussion_number]/comments.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments.js",
+ .@"pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number].js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number].js",
+ .@"pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number]/reactions.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/comments/[comment_number]/reactions.js",
+ .@"pages/teams/[team_id]/discussions/[discussion_number]/reactions.js" = "//pages/teams/[team_id]/discussions/[discussion_number]/reactions.js",
+ .@"pages/teams/[team_id]/invitations.js" = "//pages/teams/[team_id]/invitations.js",
+ .@"pages/teams/[team_id]/members.js" = "//pages/teams/[team_id]/members.js",
+ .@"pages/teams/[team_id]/members/[username].js" = "//pages/teams/[team_id]/members/[username].js",
+ .@"pages/teams/[team_id]/memberships/[username].js" = "//pages/teams/[team_id]/memberships/[username].js",
+ .@"pages/teams/[team_id]/projects.js" = "//pages/teams/[team_id]/projects.js",
+ .@"pages/teams/[team_id]/projects/[project_id].js" = "//pages/teams/[team_id]/projects/[project_id].js",
+ .@"pages/teams/[team_id]/repos.js" = "//pages/teams/[team_id]/repos.js",
+ .@"pages/teams/[team_id]/repos/[owner]/[repo].js" = "//pages/teams/[team_id]/repos/[owner]/[repo].js",
+ .@"pages/teams/[team_id]/teams.js" = "//pages/teams/[team_id]/teams.js",
+ .@"pages/users.js" = "//pages/users.js",
+ .@"pages/users/[username].js" = "//pages/users/[username].js",
+ .@"pages/users/[username]/events.js" = "//pages/users/[username]/events.js",
+ .@"pages/users/[username]/events/public.js" = "//pages/users/[username]/events/public.js",
+ .@"pages/users/[username]/followers.js" = "//pages/users/[username]/followers.js",
+ .@"pages/users/[username]/following.js" = "//pages/users/[username]/following.js",
+ .@"pages/users/[username]/following/[target_user].js" = "//pages/users/[username]/following/[target_user].js",
+ .@"pages/users/[username]/gpg_keys.js" = "//pages/users/[username]/gpg_keys.js",
+ .@"pages/users/[username]/keys.js" = "//pages/users/[username]/keys.js",
+ .@"pages/users/[username]/orgs.js" = "//pages/users/[username]/orgs.js",
+ .@"pages/users/[username]/received_events.js" = "//pages/users/[username]/received_events.js",
+ .@"pages/users/[username]/received_events/public.js" = "//pages/users/[username]/received_events/public.js",
+ .@"pages/users/[username]/repos.js" = "//pages/users/[username]/repos.js",
+ .@"pages/users/[username]/starred.js" = "//pages/users/[username]/starred.js",
+ .@"pages/users/[username]/subscriptions.js" = "//pages/users/[username]/subscriptions.js",
+ .@"pages/zen.js" = "//pages/zen.js",
+};
+
+// copy((await next.router.pageLoader.getPageList()).sort())
+pub const vercel_routes_list = .{
+ .@"pages/index.js" = "// pages/index.js",
+ .@"pages/404.js" = "// pages/404.js",
+ .@"pages/[teamSlug].js" = "// pages/[teamSlug].js",
+ .@"pages/[teamSlug]/[project].js" = "// pages/[teamSlug]/[project].js",
+ .@"pages/[teamSlug]/[project]/[id].js" = "// pages/[teamSlug]/[project]/[id].js",
+ .@"pages/[teamSlug]/[project]/[id]/[suffix].js" = "// pages/[teamSlug]/[project]/[id]/[suffix].js",
+ .@"pages/[teamSlug]/[project]/[id]/[suffix]/functions.js" = "// pages/[teamSlug]/[project]/[id]/[suffix]/functions.js",
+ .@"pages/[teamSlug]/[project]/[id]/[suffix]/source.js" = "// pages/[teamSlug]/[project]/[id]/[suffix]/source.js",
+ .@"pages/[teamSlug]/[project]/[id]/functions.js" = "// pages/[teamSlug]/[project]/[id]/functions.js",
+ .@"pages/[teamSlug]/[project]/[id]/source.js" = "// pages/[teamSlug]/[project]/[id]/source.js",
+ .@"pages/[teamSlug]/[project]/deployments.js" = "// pages/[teamSlug]/[project]/deployments.js",
+ .@"pages/[teamSlug]/[project]/domains.js" = "// pages/[teamSlug]/[project]/domains.js",
+ .@"pages/[teamSlug]/[project]/insights.js" = "// pages/[teamSlug]/[project]/insights.js",
+ .@"pages/[teamSlug]/[project]/integrations/[cfgId].js" = "// pages/[teamSlug]/[project]/integrations/[cfgId].js",
+ .@"pages/[teamSlug]/[project]/settings.js" = "// pages/[teamSlug]/[project]/settings.js",
+ .@"pages/[teamSlug]/[project]/settings/advanced.js" = "// pages/[teamSlug]/[project]/settings/advanced.js",
+ .@"pages/[teamSlug]/[project]/settings/domains.js" = "// pages/[teamSlug]/[project]/settings/domains.js",
+ .@"pages/[teamSlug]/[project]/settings/environment-variables.js" = "// pages/[teamSlug]/[project]/settings/environment-variables.js",
+ .@"pages/[teamSlug]/[project]/settings/general.js" = "// pages/[teamSlug]/[project]/settings/general.js",
+ .@"pages/[teamSlug]/[project]/settings/git.js" = "// pages/[teamSlug]/[project]/settings/git.js",
+ .@"pages/[teamSlug]/[project]/settings/integrations.js" = "// pages/[teamSlug]/[project]/settings/integrations.js",
+ .@"pages/[teamSlug]/[project]/settings/security.js" = "// pages/[teamSlug]/[project]/settings/security.js",
+ .@"pages/[teamSlug]/[project]/settings/serverless-functions.js" = "// pages/[teamSlug]/[project]/settings/serverless-functions.js",
+ .@"pages/_app.js" = "// pages/_app.js",
+ .@"pages/_error.js" = "// pages/_error.js",
+ .@"pages/_flags.js" = "// pages/_flags.js",
+ .@"pages/about.js" = "// pages/about.js",
+ .@"pages/about/[member].js" = "// pages/about/[member].js",
+ .@"pages/account.js" = "// pages/account.js",
+ .@"pages/account/billing.js" = "// pages/account/billing.js",
+ .@"pages/account/general.js" = "// pages/account/general.js",
+ .@"pages/account/git.js" = "// pages/account/git.js",
+ .@"pages/account/invoices.js" = "// pages/account/invoices.js",
+ .@"pages/account/login-connections.js" = "// pages/account/login-connections.js",
+ .@"pages/account/teams.js" = "// pages/account/teams.js",
+ .@"pages/account/tokens.js" = "// pages/account/tokens.js",
+ .@"pages/blog.js" = "// pages/blog.js",
+ .@"pages/blog/[post].js" = "// pages/blog/[post].js",
+ .@"pages/blog/preview/[post].js" = "// pages/blog/preview/[post].js",
+ .@"pages/careers.js" = "// pages/careers.js",
+ .@"pages/careers/[slug].js" = "// pages/careers/[slug].js",
+ .@"pages/careers/[slug]/preview.js" = "// pages/careers/[slug]/preview.js",
+ .@"pages/careers/preview.js" = "// pages/careers/preview.js",
+ .@"pages/case-studies/apideck.js" = "// pages/case-studies/apideck.js",
+ .@"pages/case-studies/barnebys.js" = "// pages/case-studies/barnebys.js",
+ .@"pages/case-studies/cssbattle.js" = "// pages/case-studies/cssbattle.js",
+ .@"pages/case-studies/dashing.js" = "// pages/case-studies/dashing.js",
+ .@"pages/case-studies/fireflies.js" = "// pages/case-studies/fireflies.js",
+ .@"pages/case-studies/fitt.js" = "// pages/case-studies/fitt.js",
+ .@"pages/case-studies/flowkit.js" = "// pages/case-studies/flowkit.js",
+ .@"pages/case-studies/integral.js" = "// pages/case-studies/integral.js",
+ .@"pages/case-studies/labelbox.js" = "// pages/case-studies/labelbox.js",
+ .@"pages/case-studies/mural.js" = "// pages/case-studies/mural.js",
+ .@"pages/case-studies/unspent.js" = "// pages/case-studies/unspent.js",
+ .@"pages/changelog.js" = "// pages/changelog.js",
+ .@"pages/changelog/[item].js" = "// pages/changelog/[item].js",
+ .@"pages/changelog/preview/[item].js" = "// pages/changelog/preview/[item].js",
+ .@"pages/cli.js" = "// pages/cli.js",
+ .@"pages/communications-center.js" = "// pages/communications-center.js",
+ .@"pages/confirm.js" = "// pages/confirm.js",
+ .@"pages/contact.js" = "// pages/contact.js",
+ .@"pages/contact/sales.js" = "// pages/contact/sales.js",
+ .@"pages/customers.js" = "// pages/customers.js",
+ .@"pages/customers/[slug].js" = "// pages/customers/[slug].js",
+ .@"pages/customers/preview/[slug].js" = "// pages/customers/preview/[slug].js",
+ .@"pages/dashboard.js" = "// pages/dashboard.js",
+ .@"pages/dashboard/[teamSlug]/activity.js" = "// pages/dashboard/[teamSlug]/activity.js",
+ .@"pages/dashboard/[teamSlug]/domains.js" = "// pages/dashboard/[teamSlug]/domains.js",
+ .@"pages/dashboard/[teamSlug]/domains/[domain].js" = "// pages/dashboard/[teamSlug]/domains/[domain].js",
+ .@"pages/dashboard/[teamSlug]/domains/delegate.js" = "// pages/dashboard/[teamSlug]/domains/delegate.js",
+ .@"pages/dashboard/[teamSlug]/integrations.js" = "// pages/dashboard/[teamSlug]/integrations.js",
+ .@"pages/dashboard/[teamSlug]/integrations/[cfgId].js" = "// pages/dashboard/[teamSlug]/integrations/[cfgId].js",
+ .@"pages/dashboard/[teamSlug]/integrations/[cfgId]/installed.js" = "// pages/dashboard/[teamSlug]/integrations/[cfgId]/installed.js",
+ .@"pages/dashboard/[teamSlug]/integrations/configuration.js" = "// pages/dashboard/[teamSlug]/integrations/configuration.js",
+ .@"pages/dashboard/[teamSlug]/integrations/console.js" = "// pages/dashboard/[teamSlug]/integrations/console.js",
+ .@"pages/dashboard/[teamSlug]/integrations/console/[slug].js" = "// pages/dashboard/[teamSlug]/integrations/console/[slug].js",
+ .@"pages/dashboard/[teamSlug]/integrations/create.js" = "// pages/dashboard/[teamSlug]/integrations/create.js",
+ .@"pages/dashboard/[teamSlug]/integrations/edit.js" = "// pages/dashboard/[teamSlug]/integrations/edit.js",
+ .@"pages/dashboard/[teamSlug]/project/deployments.js" = "// pages/dashboard/[teamSlug]/project/deployments.js",
+ .@"pages/dashboard/[teamSlug]/project/integrations/configuration.js" = "// pages/dashboard/[teamSlug]/project/integrations/configuration.js",
+ .@"pages/dashboard/[teamSlug]/usage.js" = "// pages/dashboard/[teamSlug]/usage.js",
+ .@"pages/dashboard/activity.js" = "// pages/dashboard/activity.js",
+ .@"pages/dashboard/domains.js" = "// pages/dashboard/domains.js",
+ .@"pages/dashboard/domains/[domain].js" = "// pages/dashboard/domains/[domain].js",
+ .@"pages/dashboard/domains/delegate.js" = "// pages/dashboard/domains/delegate.js",
+ .@"pages/dashboard/integrations.js" = "// pages/dashboard/integrations.js",
+ .@"pages/dashboard/integrations/[cfgId].js" = "// pages/dashboard/integrations/[cfgId].js",
+ .@"pages/dashboard/integrations/[cfgId]/installed.js" = "// pages/dashboard/integrations/[cfgId]/installed.js",
+ .@"pages/dashboard/integrations/console.js" = "// pages/dashboard/integrations/console.js",
+ .@"pages/dashboard/integrations/console/[slug].js" = "// pages/dashboard/integrations/console/[slug].js",
+ .@"pages/dashboard/integrations/create.js" = "// pages/dashboard/integrations/create.js",
+ .@"pages/dashboard/integrations/edit.js" = "// pages/dashboard/integrations/edit.js",
+ .@"pages/dashboard/integrations/installed.js" = "// pages/dashboard/integrations/installed.js",
+ .@"pages/dashboard/project/deployments.js" = "// pages/dashboard/project/deployments.js",
+ .@"pages/dashboard/project/insights.js" = "// pages/dashboard/project/insights.js",
+ .@"pages/dashboard/project/integrations/[cfgId].js" = "// pages/dashboard/project/integrations/[cfgId].js",
+ .@"pages/dashboard/usage.js" = "// pages/dashboard/usage.js",
+ .@"pages/deployment/redirect.js" = "// pages/deployment/redirect.js",
+ .@"pages/deployments/[host]/source.js" = "// pages/deployments/[host]/source.js",
+ .@"pages/design.js" = "// pages/design.js",
+ .@"pages/design/autocomplete.js" = "// pages/design/autocomplete.js",
+ .@"pages/design/avatar.js" = "// pages/design/avatar.js",
+ .@"pages/design/badge.js" = "// pages/design/badge.js",
+ .@"pages/design/brands.js" = "// pages/design/brands.js",
+ .@"pages/design/button.js" = "// pages/design/button.js",
+ .@"pages/design/calendar.js" = "// pages/design/calendar.js",
+ .@"pages/design/capacity.js" = "// pages/design/capacity.js",
+ .@"pages/design/checkbox.js" = "// pages/design/checkbox.js",
+ .@"pages/design/collapse.js" = "// pages/design/collapse.js",
+ .@"pages/design/color.js" = "// pages/design/color.js",
+ .@"pages/design/combobox.js" = "// pages/design/combobox.js",
+ .@"pages/design/description.js" = "// pages/design/description.js",
+ .@"pages/design/drawer.js" = "// pages/design/drawer.js",
+ .@"pages/design/entity.js" = "// pages/design/entity.js",
+ .@"pages/design/error.js" = "// pages/design/error.js",
+ .@"pages/design/feedback.js" = "// pages/design/feedback.js",
+ .@"pages/design/fieldset.js" = "// pages/design/fieldset.js",
+ .@"pages/design/file-tree.js" = "// pages/design/file-tree.js",
+ .@"pages/design/footer.js" = "// pages/design/footer.js",
+ .@"pages/design/grid.js" = "// pages/design/grid.js",
+ .@"pages/design/icon.js" = "// pages/design/icon.js",
+ .@"pages/design/icons.js" = "// pages/design/icons.js",
+ .@"pages/design/image.js" = "// pages/design/image.js",
+ .@"pages/design/input.js" = "// pages/design/input.js",
+ .@"pages/design/keyboard-input.js" = "// pages/design/keyboard-input.js",
+ .@"pages/design/link.js" = "// pages/design/link.js",
+ .@"pages/design/loading-dots.js" = "// pages/design/loading-dots.js",
+ .@"pages/design/marketing-text.js" = "// pages/design/marketing-text.js",
+ .@"pages/design/menu.js" = "// pages/design/menu.js",
+ .@"pages/design/modal.js" = "// pages/design/modal.js",
+ .@"pages/design/nextjs.js" = "// pages/design/nextjs.js",
+ .@"pages/design/note.js" = "// pages/design/note.js",
+ .@"pages/design/playground.js" = "// pages/design/playground.js",
+ .@"pages/design/popover.js" = "// pages/design/popover.js",
+ .@"pages/design/popover-menu.js" = "// pages/design/popover-menu.js",
+ .@"pages/design/progress.js" = "// pages/design/progress.js",
+ .@"pages/design/radio.js" = "// pages/design/radio.js",
+ .@"pages/design/scroller.js" = "// pages/design/scroller.js",
+ .@"pages/design/select.js" = "// pages/design/select.js",
+ .@"pages/design/show-more.js" = "// pages/design/show-more.js",
+ .@"pages/design/skeleton.js" = "// pages/design/skeleton.js",
+ .@"pages/design/snippet.js" = "// pages/design/snippet.js",
+ .@"pages/design/spacer.js" = "// pages/design/spacer.js",
+ .@"pages/design/spinner.js" = "// pages/design/spinner.js",
+ .@"pages/design/stack.js" = "// pages/design/stack.js",
+ .@"pages/design/status-dot.js" = "// pages/design/status-dot.js",
+ .@"pages/design/switch.js" = "// pages/design/switch.js",
+ .@"pages/design/table.js" = "// pages/design/table.js",
+ .@"pages/design/tabs.js" = "// pages/design/tabs.js",
+ .@"pages/design/tag.js" = "// pages/design/tag.js",
+ .@"pages/design/text.js" = "// pages/design/text.js",
+ .@"pages/design/textarea.js" = "// pages/design/textarea.js",
+ .@"pages/design/toast.js" = "// pages/design/toast.js",
+ .@"pages/design/toggle.js" = "// pages/design/toggle.js",
+ .@"pages/design/tooltip.js" = "// pages/design/tooltip.js",
+ .@"pages/design/ui.js" = "// pages/design/ui.js",
+ .@"pages/design/video.js" = "// pages/design/video.js",
+ .@"pages/design/video-card.js" = "// pages/design/video-card.js",
+ .@"pages/design/window.js" = "// pages/design/window.js",
+ .@"pages/domains.js" = "// pages/domains.js",
+ .@"pages/experts.js" = "// pages/experts.js",
+ .@"pages/experts/[slug].js" = "// pages/experts/[slug].js",
+ .@"pages/experts/[slug]/projects/[projectSlug].js" = "// pages/experts/[slug]/projects/[projectSlug].js",
+ .@"pages/git/authorize.js" = "// pages/git/authorize.js",
+ .@"pages/github/installation-approved.js" = "// pages/github/installation-approved.js",
+ .@"pages/github/installation-requested.js" = "// pages/github/installation-requested.js",
+ .@"pages/github/installed.js" = "// pages/github/installed.js",
+ .@"pages/hackathon.js" = "// pages/hackathon.js",
+ .@"pages/home.js" = "// pages/home.js",
+ .@"pages/import/[teamSlug].js" = "// pages/import/[teamSlug].js",
+ .@"pages/insights.js" = "// pages/insights.js",
+ .@"pages/integrations.js" = "// pages/integrations.js",
+ .@"pages/integrations/[slug].js" = "// pages/integrations/[slug].js",
+ .@"pages/integrations/[slug]/loading.js" = "// pages/integrations/[slug]/loading.js",
+ .@"pages/integrations/[slug]/new.js" = "// pages/integrations/[slug]/new.js",
+ .@"pages/keyboard.js" = "// pages/keyboard.js",
+ .@"pages/legal/cookie-policy.js" = "// pages/legal/cookie-policy.js",
+ .@"pages/legal/dmca-policy.js" = "// pages/legal/dmca-policy.js",
+ .@"pages/legal/dpa.js" = "// pages/legal/dpa.js",
+ .@"pages/legal/enterprise-terms.js" = "// pages/legal/enterprise-terms.js",
+ .@"pages/legal/government-requests.js" = "// pages/legal/government-requests.js",
+ .@"pages/legal/inactivity-policy.js" = "// pages/legal/inactivity-policy.js",
+ .@"pages/legal/privacy-policy.js" = "// pages/legal/privacy-policy.js",
+ .@"pages/legal/sla.js" = "// pages/legal/sla.js",
+ .@"pages/legal/sub-processors.js" = "// pages/legal/sub-processors.js",
+ .@"pages/legal/support-terms.js" = "// pages/legal/support-terms.js",
+ .@"pages/legal/terms.js" = "// pages/legal/terms.js",
+ .@"pages/legal/trademark-policy.js" = "// pages/legal/trademark-policy.js",
+ .@"pages/live.js" = "// pages/live.js",
+ .@"pages/login/[[...mode]].js" = "// pages/login/[[...mode]].js",
+ .@"pages/login/scope/[teamSlug].js" = "// pages/login/scope/[teamSlug].js",
+ .@"pages/new.js" = "// pages/new.js",
+ .@"pages/new/[teamSlug].js" = "// pages/new/[teamSlug].js",
+ .@"pages/new/[teamSlug]/clone.js" = "// pages/new/[teamSlug]/clone.js",
+ .@"pages/new/[teamSlug]/git/third-party.js" = "// pages/new/[teamSlug]/git/third-party.js",
+ .@"pages/new/[teamSlug]/import.js" = "// pages/new/[teamSlug]/import.js",
+ .@"pages/new/[teamSlug]/success.js" = "// pages/new/[teamSlug]/success.js",
+ .@"pages/new/[teamSlug]/templates.js" = "// pages/new/[teamSlug]/templates.js",
+ .@"pages/new/clone.js" = "// pages/new/clone.js",
+ .@"pages/new/git/connected.js" = "// pages/new/git/connected.js",
+ .@"pages/new/git/third-party.js" = "// pages/new/git/third-party.js",
+ .@"pages/new/import.js" = "// pages/new/import.js",
+ .@"pages/new/success.js" = "// pages/new/success.js",
+ .@"pages/new/templates.js" = "// pages/new/templates.js",
+ .@"pages/notifications/account-being-deleted.js" = "// pages/notifications/account-being-deleted.js",
+ .@"pages/notifications/account-deletion.js" = "// pages/notifications/account-deletion.js",
+ .@"pages/notifications/authentication-failed.js" = "// pages/notifications/authentication-failed.js",
+ .@"pages/notifications/cli-login-failed.js" = "// pages/notifications/cli-login-failed.js",
+ .@"pages/notifications/cli-login-incomplete.js" = "// pages/notifications/cli-login-incomplete.js",
+ .@"pages/notifications/cli-login-oob.js" = "// pages/notifications/cli-login-oob.js",
+ .@"pages/notifications/cli-login-success.js" = "// pages/notifications/cli-login-success.js",
+ .@"pages/notifications/deletion-confirmed.js" = "// pages/notifications/deletion-confirmed.js",
+ .@"pages/notifications/email-change-failed.js" = "// pages/notifications/email-change-failed.js",
+ .@"pages/notifications/email-changed.js" = "// pages/notifications/email-changed.js",
+ .@"pages/notifications/email-confirmed.js" = "// pages/notifications/email-confirmed.js",
+ .@"pages/notifications/email-signup-confirmed.js" = "// pages/notifications/email-signup-confirmed.js",
+ .@"pages/notifications/token-expired.js" = "// pages/notifications/token-expired.js",
+ .@"pages/oauth/authorize.js" = "// pages/oauth/authorize.js",
+ .@"pages/oauth/git.js" = "// pages/oauth/git.js",
+ .@"pages/oauth/verify.js" = "// pages/oauth/verify.js",
+ .@"pages/oss.js" = "// pages/oss.js",
+ .@"pages/partners.js" = "// pages/partners.js",
+ .@"pages/partners/technology.js" = "// pages/partners/technology.js",
+ .@"pages/popups/saml-sso.js" = "// pages/popups/saml-sso.js",
+ .@"pages/pricing.js" = "// pages/pricing.js",
+ .@"pages/security.js" = "// pages/security.js",
+ .@"pages/signup/[[...mode]].js" = "// pages/signup/[[...mode]].js",
+ .@"pages/solutions/angular.js" = "// pages/solutions/angular.js",
+ .@"pages/solutions/gatsby.js" = "// pages/solutions/gatsby.js",
+ .@"pages/solutions/nextjs.js" = "// pages/solutions/nextjs.js",
+ .@"pages/solutions/react.js" = "// pages/solutions/react.js",
+ .@"pages/solutions/vue.js" = "// pages/solutions/vue.js",
+ .@"pages/support/request.js" = "// pages/support/request.js",
+ .@"pages/teams.js" = "// pages/teams.js",
+ .@"pages/teams/[teamSlug]/settings.js" = "// pages/teams/[teamSlug]/settings.js",
+ .@"pages/teams/[teamSlug]/settings/billing.js" = "// pages/teams/[teamSlug]/settings/billing.js",
+ .@"pages/teams/[teamSlug]/settings/general.js" = "// pages/teams/[teamSlug]/settings/general.js",
+ .@"pages/teams/[teamSlug]/settings/git.js" = "// pages/teams/[teamSlug]/settings/git.js",
+ .@"pages/teams/[teamSlug]/settings/invoices.js" = "// pages/teams/[teamSlug]/settings/invoices.js",
+ .@"pages/teams/[teamSlug]/settings/members.js" = "// pages/teams/[teamSlug]/settings/members.js",
+ .@"pages/teams/[teamSlug]/settings/security.js" = "// pages/teams/[teamSlug]/settings/security.js",
+ .@"pages/teams/connect.js" = "// pages/teams/connect.js",
+ .@"pages/teams/create.js" = "// pages/teams/create.js",
+ .@"pages/teams/invite.js" = "// pages/teams/invite.js",
+ .@"pages/teams/invite/[inviteCode].js" = "// pages/teams/invite/[inviteCode].js",
+ .@"pages/tv.js" = "// pages/tv.js",
+ .@"pages/tv/[video].js" = "// pages/tv/[video].js",
+ .@"pages/vercel-live-auth.js" = "// pages/vercel-live-auth.js",
+ .@"pages/virtual-event-starter-ki.js" = "// pages/virtual-event-starter-ki.js",
+};