@marko/run 0.0.1-beta3 → 0.0.1-beta5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,173 @@ import { fileURLToPath } from "url";
4
4
 
5
5
  // src/adapter/dev-server.ts
6
6
  import { createServer } from "vite";
7
- import createMiddleware from "@marko/run/adapter/middleware";
7
+
8
+ // src/adapter/middleware.ts
9
+ import * as webStream from "stream/web";
10
+ import installCrypto from "@hattip/polyfills/crypto";
11
+ installCrypto();
12
+ for (const key of Object.keys(webStream)) {
13
+ if (!(key in global)) {
14
+ global[key] = webStream[key];
15
+ }
16
+ }
17
+ function getForwardedHeader(req, name) {
18
+ const value = req.headers["x-forwarded-" + name];
19
+ if (value) {
20
+ if (typeof value === "string") {
21
+ const index = value.indexOf(",");
22
+ return index < 0 ? value : value.slice(0, index);
23
+ }
24
+ return value[0];
25
+ }
26
+ }
27
+ function getOrigin(req, protocol, host, trustProxy) {
28
+ var _a;
29
+ protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
30
+ host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
31
+ if (!host) {
32
+ if (process.env.NODE_ENV !== "production") {
33
+ host = "localhost";
34
+ console.warn(
35
+ `Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
36
+ );
37
+ } else {
38
+ throw new Error(
39
+ `Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
40
+ );
41
+ }
42
+ }
43
+ return `${protocol}://${host}`;
44
+ }
45
+ function createMiddleware(router, options = {}) {
46
+ const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
47
+ let { origin = process.env.ORIGIN } = options;
48
+ let protocol;
49
+ let host;
50
+ if (origin) {
51
+ ({ protocol, host } = new URL(origin));
52
+ protocol = protocol.slice(0, -1);
53
+ }
54
+ return async (req, res, next) => {
55
+ origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
56
+ const url = new URL(req.url, origin);
57
+ const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
58
+ const requestContext = {
59
+ method: req.method,
60
+ url,
61
+ platform: {
62
+ ip,
63
+ request: req,
64
+ response: res,
65
+ setCookie(cookie) {
66
+ res.appendHeader("set-cookie", cookie);
67
+ }
68
+ }
69
+ };
70
+ Object.defineProperty(requestContext, "request", {
71
+ get() {
72
+ const headers = req.headers;
73
+ const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
74
+ start(controller) {
75
+ req.on("data", (chunk) => controller.enqueue(chunk));
76
+ req.on("end", () => controller.close());
77
+ req.on("error", (err) => controller.error(err));
78
+ }
79
+ });
80
+ const request = new Request(url, {
81
+ method: req.method,
82
+ headers,
83
+ body,
84
+ duplex: "half"
85
+ });
86
+ Object.defineProperty(this, "request", {
87
+ value: request,
88
+ enumerable: true,
89
+ configurable: true
90
+ });
91
+ return request;
92
+ },
93
+ enumerable: true,
94
+ configurable: true
95
+ });
96
+ const response = await router(requestContext);
97
+ if (!response) {
98
+ if (next) {
99
+ next();
100
+ } else {
101
+ res.statusCode = 404;
102
+ res.setHeader("content-length", "0");
103
+ res.end();
104
+ return;
105
+ }
106
+ return;
107
+ }
108
+ res.statusCode = response.status;
109
+ for (const [key, value] of response.headers) {
110
+ if (key === "set-cookie") {
111
+ let sepIndex = value.indexOf(",") + 1;
112
+ if (!sepIndex) {
113
+ res.setHeader(key, value);
114
+ } else {
115
+ let index = 0;
116
+ do {
117
+ res.appendHeader(key, value.slice(index, sepIndex - 1));
118
+ index = sepIndex;
119
+ sepIndex = value.indexOf(",", sepIndex) + 1;
120
+ } while (sepIndex);
121
+ res.appendHeader(key, value.slice(index));
122
+ }
123
+ } else {
124
+ res.setHeader(key, value);
125
+ }
126
+ }
127
+ if (!response.body) {
128
+ if (!response.headers.has("content-length")) {
129
+ res.setHeader("content-length", "0");
130
+ }
131
+ res.end();
132
+ return;
133
+ }
134
+ const reader = response.body.getReader();
135
+ if (res.destroyed) {
136
+ reader.cancel();
137
+ return;
138
+ }
139
+ res.on("close", cancel);
140
+ res.on("error", cancel);
141
+ write();
142
+ function cancel(error) {
143
+ res.off("close", cancel);
144
+ res.off("error", cancel);
145
+ reader.cancel(error).catch(() => {
146
+ });
147
+ error && res.destroy(error);
148
+ }
149
+ async function write() {
150
+ try {
151
+ while (true) {
152
+ const { done, value } = await reader.read();
153
+ if (done) {
154
+ res.end();
155
+ return;
156
+ } else if (!res.write(value)) {
157
+ res.once("drain", write);
158
+ return;
159
+ } else if (res.flush) {
160
+ res.flush();
161
+ }
162
+ }
163
+ } catch (err) {
164
+ const error = err instanceof Error ? err : new Error("Error while writing to node response", {
165
+ cause: err
166
+ });
167
+ cancel(error);
168
+ }
169
+ }
170
+ };
171
+ }
172
+
173
+ // src/adapter/dev-server.ts
8
174
  function createViteDevMiddleware(devServer, load, factory) {
9
175
  let value;
10
176
  let middleware;
@@ -79,20 +79,19 @@ function createMiddleware(router, options = {}) {
79
79
  origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
80
80
  const url = new URL(req.url, origin);
81
81
  const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
82
- const platform = {
83
- ip,
84
- request: req,
85
- response: res,
86
- setCookie(cookie) {
87
- res.appendHeader("set-cookie", cookie);
88
- }
89
- };
90
- const context = {
82
+ const requestContext = {
91
83
  method: req.method,
92
84
  url,
93
- platform
85
+ platform: {
86
+ ip,
87
+ request: req,
88
+ response: res,
89
+ setCookie(cookie) {
90
+ res.appendHeader("set-cookie", cookie);
91
+ }
92
+ }
94
93
  };
95
- Object.defineProperty(context, "request", {
94
+ Object.defineProperty(requestContext, "request", {
96
95
  get() {
97
96
  const headers = req.headers;
98
97
  const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
@@ -118,7 +117,7 @@ function createMiddleware(router, options = {}) {
118
117
  enumerable: true,
119
118
  configurable: true
120
119
  });
121
- const response = await router(context);
120
+ const response = await router(requestContext);
122
121
  if (!response) {
123
122
  if (next) {
124
123
  next();
@@ -1,4 +1,4 @@
1
- import type { Router } from "@marko/run";
1
+ import type { Router } from "../runtime";
2
2
  import type { IncomingMessage, ServerResponse } from "http";
3
3
  declare module "net" {
4
4
  interface Socket {
@@ -14,10 +14,6 @@ declare module "http" {
14
14
  appendHeader(key: string, value: string | string[]): this;
15
15
  }
16
16
  }
17
- declare module "@marko/run" {
18
- interface Platform extends NodePlatformInfo {
19
- }
20
- }
21
17
  export interface NodePlatformInfo {
22
18
  ip: string;
23
19
  request: IncomingMessage;
@@ -48,20 +48,19 @@ function createMiddleware(router, options = {}) {
48
48
  origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
49
49
  const url = new URL(req.url, origin);
50
50
  const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
51
- const platform = {
52
- ip,
53
- request: req,
54
- response: res,
55
- setCookie(cookie) {
56
- res.appendHeader("set-cookie", cookie);
57
- }
58
- };
59
- const context = {
51
+ const requestContext = {
60
52
  method: req.method,
61
53
  url,
62
- platform
54
+ platform: {
55
+ ip,
56
+ request: req,
57
+ response: res,
58
+ setCookie(cookie) {
59
+ res.appendHeader("set-cookie", cookie);
60
+ }
61
+ }
63
62
  };
64
- Object.defineProperty(context, "request", {
63
+ Object.defineProperty(requestContext, "request", {
65
64
  get() {
66
65
  const headers = req.headers;
67
66
  const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
@@ -87,7 +86,7 @@ function createMiddleware(router, options = {}) {
87
86
  enumerable: true,
88
87
  configurable: true
89
88
  });
90
- const response = await router(context);
89
+ const response = await router(requestContext);
91
90
  if (!response) {
92
91
  if (next) {
93
92
  next();
@@ -8,14 +8,18 @@ import { build as viteBuild, resolveConfig } from "vite";
8
8
  import sade from "sade";
9
9
 
10
10
  // src/vite/utils/config.ts
11
- var KEY = "__MARKO_SERVE_OPTIONS__";
12
- function getMarkoRunOptions(viteConfig) {
13
- return viteConfig[KEY];
11
+ var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
12
+ var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
13
+ function getConfig(obj, key) {
14
+ return obj[key];
14
15
  }
15
- function setMarkoRunOptions(viteConfig, options) {
16
- viteConfig[KEY] = options;
17
- return viteConfig;
16
+ function setConfig(obj, key, value) {
17
+ obj[key] = value;
18
+ return obj;
18
19
  }
20
+ var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
21
+ var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
22
+ var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
19
23
 
20
24
  // src/cli/index.ts
21
25
  import { MemoryStore } from "@marko/vite";
@@ -91,18 +95,20 @@ function sleep(ms) {
91
95
  var __dirname = fileURLToPath(new URL(".", import.meta.url));
92
96
  var cwd = process.cwd();
93
97
  var defaultPort = +process.env.PORT || 3e3;
94
- var prog = sade("marko-run").version("0.0.1").option("-c, --config", "Provide path to a Vite config").option("-e, --env", "Provide path to a dotenv file");
95
- prog.command("preview [entry]", "", { default: true }).describe("Start production-like server against built assets").option("-o, --output", "Directory to serve files").option("-p, --port", "Port to use for dev server").option("-f, --file", "Output file to start").action(async (entry, opts) => {
98
+ var defaultConfigFileBases = ["serve.config", "vite.config"];
99
+ var defaultConfigFileExts = [".js", ".cjs", ".mjs", ".ts", ".mts"];
100
+ var prog = sade("marko-run").version("0.0.1").option("-c, --config", `Provide path to a Vite config file (by default looks for a file starting with ${defaultConfigFileBases.join(" or ")} with one of these extensions: ${defaultConfigFileExts.join(", ")})`).option("-e, --env", "Provide path to a dotenv file");
101
+ prog.command("serve [entry]", "", { default: true }).describe("Start a production-like server for already-built app files").option("-o, --output", "Directory to serve files from, and write asset files to if `--build` (default: )").option("-p, --port", "Port the server should listen on (defaults: `$PORT` env variable or 3000)").option("-f, --file", "Output file to start").action(async (entry, opts) => {
96
102
  const config2 = await getViteConfig(cwd, opts.config);
97
103
  await build(entry, config2, opts.output, false, opts.env);
98
104
  await preview(opts.entry, config2, opts.port, opts.output, opts.env);
99
105
  });
100
- prog.command("dev [entry]").describe("Start dev server").option("-p, --port", "Port to use for dev server").example("dev --config vite.config.js").action(async (entry, opts) => {
106
+ prog.command("dev [entry]").describe("Start development server in watch mode").option("-p, --port", "Port the dev server should listen on (defaults: 'preview.port' in config, or `$PORT` env variable, or 3000)").example("dev --config vite.config.js").action(async (entry, opts) => {
101
107
  const cmd = opts._.length ? `${entry} ${opts._.join(" ")}` : entry ? `node ${entry}` : void 0;
102
108
  const config2 = await getViteConfig(cwd, opts.config);
103
109
  await dev(cmd, config2, opts.port, opts.env);
104
110
  });
105
- prog.command("build [entry]").describe("Build the application").option("-o, --output", "Directory to ouput built files").option("--skip-client", "Skip the client build").example("build --config vite.config.js").action(async (entry, opts) => {
111
+ prog.command("build [entry]").describe("Build the application (without serving it)").option("-o, --output", "Directory to write built files (default: 'build.outDir' in Vite config)").option("--skip-client", "Skip the client-side build").example("build --config vite.config.js").action(async (entry, opts) => {
106
112
  const config2 = await getViteConfig(cwd, opts.config);
107
113
  await build(entry, config2, opts.ouput, opts["skip-client"], opts.env);
108
114
  });
@@ -117,9 +123,9 @@ async function preview(entry, configFile, port, outDir, envFile) {
117
123
  }
118
124
  const adapter = await resolveAdapter(resolvedConfig);
119
125
  if (!adapter) {
120
- throw new Error("No adapter specified for serve command");
126
+ throw new Error("No adapter specified for 'serve' command");
121
127
  } else if (!adapter.startPreview) {
122
- throw new Error(`Adapter ${adapter.name} does not support serve command`);
128
+ throw new Error(`Adapter ${adapter.name} does not support 'serve' command`);
123
129
  }
124
130
  const dir = path.resolve(cwd, resolvedConfig.build.outDir);
125
131
  const entryFile = entry ? path.join(dir, entry) : await findFileWithExt(dir, "index", [".mjs", ".js"]);
@@ -145,10 +151,10 @@ async function dev(cmd, configFile, port, envFile) {
145
151
  const adapter = await resolveAdapter(resolvedConfig);
146
152
  if (!adapter) {
147
153
  throw new Error(
148
- "No adapter specified for dev command without custom target"
154
+ "No adapter specified for 'dev' command without custom target"
149
155
  );
150
156
  } else if (!adapter.startDev) {
151
- throw new Error(`Adapter ${adapter.name} does not support serve command`);
157
+ throw new Error(`Adapter '${adapter.name}' does not support 'serve' command`);
152
158
  } else {
153
159
  await adapter.startDev(configFile, port, envFile);
154
160
  }
@@ -156,38 +162,39 @@ async function dev(cmd, configFile, port, envFile) {
156
162
  }
157
163
  async function build(entry, configFile, outDir, skipClient = false, envFile) {
158
164
  var _a;
165
+ const resolvedConfig = await resolveConfig(
166
+ { root: cwd, configFile },
167
+ "build"
168
+ );
169
+ const adapter = await resolveAdapter(resolvedConfig);
170
+ if (!adapter) {
171
+ throw new Error("No adapter specified for build command without entry");
172
+ }
159
173
  if (!entry) {
160
- const resolvedConfig = await resolveConfig(
161
- { root: cwd, configFile },
162
- "build"
163
- );
164
- const adapter = await resolveAdapter(resolvedConfig);
165
- if (!adapter) {
166
- throw new Error("No adapter specified for build command without entry");
167
- }
168
174
  entry = await ((_a = adapter.getEntryFile) == null ? void 0 : _a.call(adapter));
169
175
  if (!entry) {
170
176
  throw new Error(
171
- `Adapter ${adapter.name} does not support build command without entry`
177
+ `Adapter '${adapter.name}' does not support building without an entry`
172
178
  );
173
179
  }
174
180
  }
175
181
  if (envFile) {
176
182
  envFile = path.resolve(cwd, envFile);
177
183
  }
178
- const buildConfig = setMarkoRunOptions(
179
- {
180
- root: cwd,
181
- configFile,
182
- build: {
183
- ssr: false,
184
- outDir
185
- }
186
- },
187
- {
188
- store: new MemoryStore()
184
+ let buildConfig = {
185
+ root: cwd,
186
+ configFile,
187
+ build: {
188
+ ssr: false,
189
+ outDir
189
190
  }
190
- );
191
+ };
192
+ buildConfig = setExternalPluginOptions(buildConfig, {
193
+ store: new MemoryStore()
194
+ });
195
+ buildConfig = setExternalAdapterOptions(buildConfig, {
196
+ envFile
197
+ });
191
198
  await viteBuild({
192
199
  ...buildConfig,
193
200
  build: {
@@ -211,7 +218,7 @@ async function build(entry, configFile, outDir, skipClient = false, envFile) {
211
218
  });
212
219
  }
213
220
  }
214
- function findFileWithExt(dir, base, extensions = [".js", ".cjs", ".mjs", ".ts", ".mts"]) {
221
+ function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
215
222
  for (const ext of extensions) {
216
223
  const filePath = path.join(dir, base + ext);
217
224
  if (fs2.existsSync(filePath)) {
@@ -220,10 +227,11 @@ function findFileWithExt(dir, base, extensions = [".js", ".cjs", ".mjs", ".ts",
220
227
  }
221
228
  return void 0;
222
229
  }
223
- async function getViteConfig(dir, configFile, bases = ["serve.config", "vite.config"]) {
230
+ async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
224
231
  if (configFile) {
225
- if (!fs2.existsSync(path.join(dir, configFile))) {
226
- throw new Error(`Unable to load config file '${configFile}' from ${dir}`);
232
+ const configFilePath = path.join(dir, configFile);
233
+ if (!fs2.existsSync(configFilePath)) {
234
+ throw new Error(`No config file found at '${configFilePath}'`);
227
235
  }
228
236
  return configFile;
229
237
  }
@@ -236,9 +244,9 @@ async function getViteConfig(dir, configFile, bases = ["serve.config", "vite.con
236
244
  return path.join(__dirname, "default.config.mjs");
237
245
  }
238
246
  async function resolveAdapter(config2) {
239
- const options = getMarkoRunOptions(config2);
247
+ const options = getExternalPluginOptions(config2);
240
248
  if (!options) {
241
- throw new Error("Unable to resolve Marko Serve options");
249
+ throw new Error("Unable to resolve @marko/serve options");
242
250
  }
243
251
  return options.adapter;
244
252
  }
@@ -1,10 +1,17 @@
1
- import type { HandlerLike, ParamsObject, Route } from "./types";
1
+ import type { HandlerLike, ParamsObject, Route, RouteContext } from "./types";
2
2
  declare global {
3
3
  namespace Marko {
4
+ interface Global {
5
+ context: RouteContext;
6
+ }
7
+ }
8
+ namespace MarkoRun {
4
9
  interface CurrentRoute extends Route {
5
10
  }
11
+ interface CurrentContext extends RouteContext<CurrentRoute> {
12
+ }
6
13
  type Handler<Params extends ParamsObject = {}, Meta = unknown> = HandlerLike<Route<Params, Meta, string>>;
7
14
  function route<Params extends ParamsObject = {}, Meta = unknown>(handler: Handler<Params, Meta>): typeof handler;
8
15
  }
9
16
  }
10
- export type { HandlerLike, InputObject, InvokeRoute, MatchRoute, NextFunction, RequestContext, Route, RouteContext, RouteContextExtensions, RouteHandler, RouteWithHandler, Router, } from "./types";
17
+ export type { HandlerLike, InputObject, InvokeRoute, MatchRoute, NextFunction, PathTemplate, RequestContext, Route, RouteContext, RouteContextExtensions, RouteHandler, RouteWithHandler, Router, ValidateHref, ValidatePath, } from "./types";
@@ -31,8 +31,8 @@ __export(internal_exports, {
31
31
  notMatched: () => notMatched
32
32
  });
33
33
  module.exports = __toCommonJS(internal_exports);
34
- globalThis.Marko ?? (globalThis.Marko = {});
35
- globalThis.Marko.route = (handler) => handler;
34
+ globalThis.MarkoRun ?? (globalThis.MarkoRun = {});
35
+ globalThis.MarkoRun.route = (handler) => handler;
36
36
  var RequestNotHandled = Symbol();
37
37
  var RequestNotMatched = Symbol();
38
38
  function createInput(context) {
@@ -1,6 +1,6 @@
1
1
  // src/runtime/internal.ts
2
- globalThis.Marko ?? (globalThis.Marko = {});
3
- globalThis.Marko.route = (handler) => handler;
2
+ globalThis.MarkoRun ?? (globalThis.MarkoRun = {});
3
+ globalThis.MarkoRun.route = (handler) => handler;
4
4
  var RequestNotHandled = Symbol();
5
5
  var RequestNotMatched = Symbol();
6
6
  function createInput(context) {
@@ -26,7 +26,9 @@ __export(router_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(router_exports);
28
28
  function notImplemented() {
29
- throw new Error("This should have been replaced by the @marko/run plugin at build/dev time");
29
+ throw new Error(
30
+ "This should have been replaced by the @marko/run plugin at build/dev time"
31
+ );
30
32
  }
31
33
  var router = notImplemented;
32
34
  var matchRoute = notImplemented;
@@ -1,4 +1,4 @@
1
- import type { InvokeRoute, MatchRoute, Router } from './types';
2
- export declare const router: Router<import("./types").Platform>;
1
+ import type { MatchRoute, RequestContext, Route } from "./types";
2
+ export declare const router: <T>(context: RequestContext<T>) => Promise<Response | void>;
3
3
  export declare const matchRoute: MatchRoute;
4
- export declare const invokeRoute: InvokeRoute<import("./types").Platform>;
4
+ export declare const invokeRoute: <T>(route: Route | null, context: RequestContext<T>) => Promise<Response | void>;
@@ -1,6 +1,8 @@
1
1
  // src/runtime/router.ts
2
2
  function notImplemented() {
3
- throw new Error("This should have been replaced by the @marko/run plugin at build/dev time");
3
+ throw new Error(
4
+ "This should have been replaced by the @marko/run plugin at build/dev time"
5
+ );
4
6
  }
5
7
  var router = notImplemented;
6
8
  var matchRoute = notImplemented;
@@ -5,11 +5,9 @@ declare type Combine<T> = T extends object ? {
5
5
  } : T;
6
6
  export interface RouteContextExtensions {
7
7
  }
8
- export interface Platform {
9
- }
10
8
  export declare type ParamsObject = Record<string, string>;
11
9
  export declare type InputObject = Record<PropertyKey, any>;
12
- export interface RequestContext<T = Platform> {
10
+ export interface RequestContext<T = unknown> {
13
11
  url: URL;
14
12
  method: string;
15
13
  request: Request;
@@ -32,6 +30,14 @@ export interface RouteWithHandler<Params extends ParamsObject = {}, Meta = unkno
32
30
  handler: RouteHandler<this>;
33
31
  }
34
32
  export declare type MatchRoute = (method: string, pathname: string) => RouteWithHandler | null;
35
- export declare type Router<T = Platform> = (context: RequestContext<T>) => Promise<Response | void>;
36
- export declare type InvokeRoute<T = Platform> = (route: Route | null, context: RequestContext<T>) => Promise<Response | void>;
33
+ export declare type Router<T = unknown> = (context: RequestContext<T>) => Promise<Response | void>;
34
+ export declare type InvokeRoute<T = unknown> = (route: Route | null, context: RequestContext<T>) => Promise<Response | void>;
35
+ declare type Member<T, U> = T extends T ? (U extends T ? T : never) : never;
36
+ declare type Segments<T extends string, Acc extends string[] = []> = T extends "" ? Acc : T extends `${infer Left}/${infer Rest}` ? Segments<Rest, [...Acc, Left]> : [...Acc, T];
37
+ declare type GTE<A extends any[], B extends any[]> = A["length"] extends B["length"] ? 1 : A extends [infer _Ha, ...infer Ta] ? B extends [infer _Hb, ...infer Tb] ? GTE<Ta, Tb> : 1 : 0;
38
+ declare type MatchSegments<A extends string, B extends string> = A extends `${infer P}/${string}*` ? 1 extends GTE<Segments<B>, Segments<P>> ? `${P}/${string}` : never : Segments<B>["length"] extends Segments<A>["length"] ? A : never;
39
+ declare type PathPattern<T extends string> = T extends `${infer Left}/\${${string}}/${infer Rest}` ? PathPattern<`${Left}/${string}/${Rest}`> : T extends `${infer Left}/\${...${string}}` ? PathPattern<`${Left}/${string}*`> : T extends `${infer Left}/\${${string}}` ? PathPattern<`${Left}/${string}`> : T;
40
+ export declare type PathTemplate<Path extends string> = Path extends `${infer Left}/\${${string}}/${infer Rest}` ? PathTemplate<`${Left}/${string}/${Rest}`> : Path extends `${infer Left}/\${${string}}` ? PathTemplate<`${Left}/${string}`> : Path;
41
+ export declare type ValidatePath<Paths extends string, Path extends string> = Paths | (Path extends `/${string}` ? MatchSegments<Member<PathPattern<Paths>, Path>, Path> : Path);
42
+ export declare type ValidateHref<Paths extends string, Href extends string> = Href extends `${infer P}#${infer H}?${infer Q}` ? `${ValidatePath<Paths, P>}#${H}?${Q}` : Href extends `${infer P}?${infer Q}` ? `${ValidatePath<Paths, P>}?${Q}` : Href extends `${infer P}#${infer H}` ? `${ValidatePath<Paths, P>}#${H}` : ValidatePath<Paths, Href>;
37
43
  export {};