diff options
author | 2021-10-20 04:18:29 -0700 | |
---|---|---|
committer | 2021-10-20 04:18:29 -0700 | |
commit | 58b196c58310a1489b554d16ec2695c7974eaab3 (patch) | |
tree | e631543d9b9891e01bd35e5374e987ef600a524d | |
parent | 3dc53c3d1327f9e86467d6509cb94faacfd26580 (diff) | |
download | bun-58b196c58310a1489b554d16ec2695c7974eaab3.tar.gz bun-58b196c58310a1489b554d16ec2695c7974eaab3.tar.zst bun-58b196c58310a1489b554d16ec2695c7974eaab3.zip |
rewrote most of the router
-rw-r--r-- | packages/bun-framework-next/client.development.tsx | 35 | ||||
-rw-r--r-- | packages/bun-framework-next/fallback.development.tsx | 4 | ||||
-rw-r--r-- | packages/bun-framework-next/renderDocument.tsx | 12 | ||||
-rw-r--r-- | packages/bun-framework-next/server.development.tsx | 1 | ||||
-rw-r--r-- | src/api/schema.d.ts | 2 | ||||
-rw-r--r-- | src/api/schema.js | 11 | ||||
-rw-r--r-- | src/api/schema.peechy | 2 | ||||
-rw-r--r-- | src/api/schema.zig | 6 | ||||
-rw-r--r-- | src/bundler.zig | 9 | ||||
-rw-r--r-- | src/fallback.version | 2 | ||||
-rw-r--r-- | src/global.zig | 6 | ||||
-rw-r--r-- | src/http.zig | 58 | ||||
m--------- | src/javascript/jsc/WebKit | 0 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 40 | ||||
-rw-r--r-- | src/js_lexer.zig | 13 | ||||
-rw-r--r-- | src/query_string_map.zig | 22 | ||||
-rw-r--r-- | src/router.zig | 2238 | ||||
-rw-r--r-- | src/string_immutable.zig | 39 | ||||
-rw-r--r-- | src/test/fixtures.zig | 554 |
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, ¶ms_list)) |route| { + if (app.routes.matchPage(app.config.dir, ctx.url, ¶ms_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, ¶ms_list) catch { + RequestContextType.JavaScriptHandler.enqueue(ctx, server, ¶ms_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, ¶meters, 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", +}; |