@firtoz/hono-fetcher 2.8.1 → 2.8.2
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.
|
@@ -15,12 +15,22 @@ function substitutePathParams(path, params) {
|
|
|
15
15
|
path
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
|
+
function joinMountedRequest(prefix, request) {
|
|
19
|
+
const qIndex = request.indexOf("?");
|
|
20
|
+
const pathPart = qIndex === -1 ? request : request.slice(0, qIndex);
|
|
21
|
+
const queryPart = qIndex === -1 ? "" : request.slice(qIndex);
|
|
22
|
+
const normalized = pathPart.startsWith("/") ? pathPart : `/${pathPart}`;
|
|
23
|
+
const suffix = normalized === "/" ? "" : normalized;
|
|
24
|
+
if (prefix === "") {
|
|
25
|
+
return `${suffix || "/"}${queryPart}`;
|
|
26
|
+
}
|
|
27
|
+
return `${prefix}${suffix}${queryPart}`;
|
|
28
|
+
}
|
|
18
29
|
function createMountedFetcher(parentFetcher, mountPath, mountParams) {
|
|
19
30
|
const normalized = normalizeMountPath(mountPath);
|
|
20
31
|
const prefix = mountParams === void 0 ? normalized : substitutePathParams(normalized, mountParams);
|
|
21
32
|
return honoFetcher((request, init) => {
|
|
22
|
-
const
|
|
23
|
-
const url = prefix === "" ? path : `${prefix}${path}`;
|
|
33
|
+
const url = joinMountedRequest(prefix, request);
|
|
24
34
|
return parentFetcher(url, init);
|
|
25
35
|
});
|
|
26
36
|
}
|
|
@@ -37,5 +47,5 @@ function honoFetcherMounted(appOrFetcher, mountPath, mountParams) {
|
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
export { honoFetcherMounted };
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-JWX3AW5C.js.map
|
|
51
|
+
//# sourceMappingURL=chunk-JWX3AW5C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/honoFetcherMounted.ts"],"names":["HonoClass"],"mappings":";;;AAmGA,SAAS,mBAAmB,SAAA,EAA2B;AACtD,EAAA,MAAM,OAAA,GAAU,UAAU,IAAA,EAAK;AAC/B,EAAA,IAAI,OAAA,KAAY,EAAA,IAAM,OAAA,KAAY,GAAA,EAAK;AACtC,IAAA,OAAO,EAAA;AAAA,EACR;AACA,EAAA,MAAM,cAAc,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,GAAI,OAAA,GAAU,IAAI,OAAO,CAAA,CAAA;AACnE,EAAA,OAAO,WAAA,CAAY,SAAS,GAAG,CAAA,GAAI,YAAY,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,WAAA;AAC/D;AAEA,SAAS,oBAAA,CACR,MACA,MAAA,EACS;AACT,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAA;AAAA,IAC7B,CAAC,GAAA,EAAK,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM,GAAA,CAAI,OAAA,CAAQ,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,IACnD;AAAA,GACD;AACD;AAEA,SAAS,kBAAA,CAAmB,QAAgB,OAAA,EAAyB;AACpE,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAClC,EAAA,MAAM,WAAW,MAAA,KAAW,EAAA,GAAK,UAAU,OAAA,CAAQ,KAAA,CAAM,GAAG,MAAM,CAAA;AAClE,EAAA,MAAM,YAAY,MAAA,KAAW,EAAA,GAAK,EAAA,GAAK,OAAA,CAAQ,MAAM,MAAM,CAAA;AAC3D,EAAA,MAAM,aAAa,QAAA,CAAS,UAAA,CAAW,GAAG,CAAA,GAAI,QAAA,GAAW,IAAI,QAAQ,CAAA,CAAA;AACrE,EAAA,MAAM,MAAA,GAAS,UAAA,KAAe,GAAA,GAAM,EAAA,GAAK,UAAA;AAEzC,EAAA,IAAI,WAAW,EAAA,EAAI;AAClB,IAAA,OAAO,CAAA,EAAG,MAAA,IAAU,GAAG,CAAA,EAAG,SAAS,CAAA,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,MAAM,GAAG,SAAS,CAAA,CAAA;AACtC;AAOA,SAAS,oBAAA,CAIR,aAAA,EACA,SAAA,EACA,WAAA,EAGC;AACD,EAAA,MAAM,UAAA,GAAa,mBAAmB,SAAS,CAAA;AAC/C,EAAA,MAAM,SACL,WAAA,KAAgB,MAAA,GACb,UAAA,GACA,oBAAA,CAAqB,YAAY,WAAqC,CAAA;AAC1E,EAAA,OAAO,WAAA,CAAqC,CAAC,OAAA,EAAS,IAAA,KAAS;AAC9D,IAAA,MAAM,GAAA,GAAM,kBAAA,CAAmB,MAAA,EAAQ,OAAO,CAAA;AAC9C,IAAA,OAAO,aAAA,CAAc,KAAK,IAAI,CAAA;AAAA,EAG/B,CAAC,CAAA;AAGF;AAEA,SAAS,UAAU,KAAA,EAA4C;AAC9D,EAAA,OAAO,KAAA,YAAiBA,IAAA;AACzB;AA2DO,SAAS,kBAAA,CAIf,YAAA,EACA,SAAA,EACA,WAAA,EAC8D;AAC9D,EAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,YAAY,CAAA,GACzC,CAAC,GAAA,EAAa,IAAA,KAAuB,YAAA,CAAa,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA,GACnE,YAAA;AACH,EAAA,OAAO,oBAAA;AAAA,IACN,aAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACD;AACD","file":"chunk-JWX3AW5C.js","sourcesContent":["import type { Hono } from \"hono\";\nimport { Hono as HonoClass } from \"hono\";\nimport type { Env, Schema } from \"hono/types\";\nimport type { ExtractSchema } from \"hono/types\";\nimport {\n\thonoFetcher,\n\ttype ParsePathParams,\n\ttype TypedHonoFetcher,\n} from \"./honoFetcher\";\n\ntype SchemaRouteKeys<T extends Hono> = string & keyof ExtractSchema<T>;\n\n/**\n * Client-side view of a Hono app: same routes/schema as the server app but without\n * server `Bindings` (service-binding and browser clients do not send Worker env).\n *\n * Use with a **sub-app** type when the mount path is only on the worker (e.g. mount\n * `/admin` but routes on the type are `/users`). For typed mount prefixes derived from\n * full worker paths, pass the **worker app** type and use {@link MountedClientApp}.\n */\nexport type HonoClientApp<T extends Hono> =\n\tT extends Hono<\n\t\tinfer E extends Env,\n\t\tinfer S extends Schema,\n\t\tinfer BasePath extends string\n\t>\n\t\t? Hono<Omit<E, \"Bindings\">, S, BasePath>\n\t\t: never;\n\ntype HasMorePathSegments<Rest extends string> = Rest extends\n\t| `${string}/${string}`\n\t| `:${string}/${string}`\n\t? true\n\t: false;\n\ntype MountPrefixesWithPrefix<\n\tPrefix extends string,\n\tRoute extends string,\n> = Route extends `/${infer Seg}/${infer Rest}`\n\t? HasMorePathSegments<Rest> extends true\n\t\t?\n\t\t\t\t| `${Prefix}/${Seg}`\n\t\t\t\t| MountPrefixesWithPrefix<`${Prefix}/${Seg}`, `/${Rest}`>\n\t\t: `${Prefix}/${Seg}`\n\t: never;\n\n/** Every mount prefix along a route (`/a/b/c` → `/a` | `/a/b`). */\ntype MountPrefixesOfRoute<Route extends string> =\n\tRoute extends `/${infer Seg}/${infer Rest}`\n\t\t? HasMorePathSegments<Rest> extends true\n\t\t\t? `/${Seg}` | MountPrefixesWithPrefix<`/${Seg}`, `/${Rest}`>\n\t\t\t: `/${Seg}`\n\t\t: never;\n\n/**\n * Mount prefix valid when the worker app schema has routes under `${M}/…`.\n * Includes multi-segment prefixes (`/nested/deep`) and param segments (`/level1/:param`).\n * Excludes top-level-only routes (`/users`, `/x`) — use {@link honoFetcher} for those.\n */\nexport type ValidMountPrefix<T extends Hono> = MountPrefixesOfRoute<\n\tSchemaRouteKeys<T>\n>;\n\n/** Path params required when the mount path contains `:param` segments. */\nexport type MountPathParams<M extends string> = ParsePathParams<M>;\n\ntype StripMountRoute<\n\tRoute extends string,\n\tMount extends string,\n> = Route extends `${Mount}/${infer Rest}` ? `/${Rest}` : never;\n\ntype MountedSchema<T extends Hono, Mount extends string> = {\n\t[Route in SchemaRouteKeys<T> as StripMountRoute<\n\t\tRoute,\n\t\tMount\n\t> extends infer Stripped extends string\n\t\t? Stripped\n\t\t: never]: ExtractSchema<T>[Route];\n} & Schema;\n\n/**\n * Client view of routes under `Mount` on a **worker app** whose schema keys are full\n * paths (e.g. `/admin/users` → client `url: \"/users\"` when `Mount` is `\"/admin\"`).\n */\nexport type MountedClientApp<T extends Hono, Mount extends string> =\n\tT extends Hono<\n\t\tinfer E extends Env,\n\t\tinfer _S extends Schema,\n\t\tinfer BasePath extends string\n\t>\n\t\t? Hono<Omit<E, \"Bindings\">, MountedSchema<T, Mount>, BasePath>\n\t\t: never;\n\ntype ClientAppForMount<T extends Hono, M extends string> =\n\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>;\n\ntype MountParamsArg<M extends string> =\n\tParsePathParams<M> extends never ? undefined : ParsePathParams<M>;\n\nfunction normalizeMountPath(mountPath: string): string {\n\tconst trimmed = mountPath.trim();\n\tif (trimmed === \"\" || trimmed === \"/\") {\n\t\treturn \"\";\n\t}\n\tconst withLeading = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n\treturn withLeading.endsWith(\"/\") ? withLeading.slice(0, -1) : withLeading;\n}\n\nfunction substitutePathParams(\n\tpath: string,\n\tparams: Record<string, string>,\n): string {\n\treturn Object.entries(params).reduce(\n\t\t(acc, [key, value]) => acc.replace(`:${key}`, value),\n\t\tpath,\n\t);\n}\n\nfunction joinMountedRequest(prefix: string, request: string): string {\n\tconst qIndex = request.indexOf(\"?\");\n\tconst pathPart = qIndex === -1 ? request : request.slice(0, qIndex);\n\tconst queryPart = qIndex === -1 ? \"\" : request.slice(qIndex);\n\tconst normalized = pathPart.startsWith(\"/\") ? pathPart : `/${pathPart}`;\n\tconst suffix = normalized === \"/\" ? \"\" : normalized;\n\n\tif (prefix === \"\") {\n\t\treturn `${suffix || \"/\"}${queryPart}`;\n\t}\n\treturn `${prefix}${suffix}${queryPart}`;\n}\n\ntype ParentFetcher = (\n\trequest: string,\n\tinit?: RequestInit,\n) => Response | Promise<Response>;\n\nfunction createMountedFetcher<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T> | string,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n\tmountParams?: MountParamsArg<M>,\n): TypedHonoFetcher<\n\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>\n> {\n\tconst normalized = normalizeMountPath(mountPath);\n\tconst prefix =\n\t\tmountParams === undefined\n\t\t\t? normalized\n\t\t\t: substitutePathParams(normalized, mountParams as Record<string, string>);\n\treturn honoFetcher<ClientAppForMount<T, M>>((request, init) => {\n\t\tconst url = joinMountedRequest(prefix, request);\n\t\treturn parentFetcher(url, init) as ReturnType<\n\t\t\tClientAppForMount<T, M>[\"request\"]\n\t\t>;\n\t}) as TypedHonoFetcher<\n\t\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>\n\t>;\n}\n\nfunction isHonoApp(value: ParentFetcher | Hono): value is Hono {\n\treturn value instanceof HonoClass;\n}\n\n/**\n * Typed client for routes under `mountPath`, using `app.request` as transport.\n * Infers both the app schema and (when valid) stripped mount paths — no type args needed.\n *\n * When `mountPath` contains `:param` segments, pass `mountParams` (same shape as route\n * `params` on {@link honoFetcher}).\n */\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tapp: T,\n\tmountPath: M,\n\tmountParams: MountPathParams<M>,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(app: T, mountPath: M): TypedHonoFetcher<MountedClientApp<T, M>>;\n\n/**\n * Typed client for routes under `mountPath` on a parent fetcher.\n *\n * Use when transport is not `app.request` (service bindings, DO stubs, browser `fetch`, etc.).\n *\n * - **Worker app type** (schema keys include the mount, e.g. `/admin/users`): pass\n * `typeof workerApp` and a {@link ValidMountPrefix}; client `url` values are paths\n * **after** the mount (`/users`, not `/admin/users`). Prefer {@link honoFetcherMounted}\n * with the app instance when using `app.request`.\n * - **Sub-app type** (routes are `/users` on the sub-app, mount is only on the worker):\n * pass `typeof adminRoutes` and the worker mount string (`\"/admin\"`); client URLs\n * match the sub-app schema.\n */\nexport function honoFetcherMounted<T extends Hono>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: string,\n\tmountParams?: Record<string, string>,\n): TypedHonoFetcher<HonoClientApp<T>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n\tmountParams: MountPathParams<M>,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T> | string,\n>(\n\tappOrFetcher: T | ParentFetcher,\n\tmountPath: M,\n\tmountParams?: MountParamsArg<M>,\n): TypedHonoFetcher<HonoClientApp<T> | MountedClientApp<T, M>> {\n\tconst parentFetcher = isHonoApp(appOrFetcher)\n\t\t? (url: string, init?: RequestInit) => appOrFetcher.request(url, init)\n\t\t: appOrFetcher;\n\treturn createMountedFetcher<T, M>(\n\t\tparentFetcher,\n\t\tmountPath,\n\t\tmountParams,\n\t) as TypedHonoFetcher<HonoClientApp<T> | MountedClientApp<T, M>>;\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { honoDirectFetcher } from './chunk-QEZAY63D.js';
|
|
2
2
|
export { honoDoFetcher, honoDoFetcherWithId, honoDoFetcherWithName } from './chunk-CHQGE36J.js';
|
|
3
|
-
export { honoFetcherMounted } from './chunk-
|
|
3
|
+
export { honoFetcherMounted } from './chunk-JWX3AW5C.js';
|
|
4
4
|
export { honoFetcher } from './chunk-IL7N6DHV.js';
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -116,6 +116,19 @@ function substitutePathParams(
|
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
function joinMountedRequest(prefix: string, request: string): string {
|
|
120
|
+
const qIndex = request.indexOf("?");
|
|
121
|
+
const pathPart = qIndex === -1 ? request : request.slice(0, qIndex);
|
|
122
|
+
const queryPart = qIndex === -1 ? "" : request.slice(qIndex);
|
|
123
|
+
const normalized = pathPart.startsWith("/") ? pathPart : `/${pathPart}`;
|
|
124
|
+
const suffix = normalized === "/" ? "" : normalized;
|
|
125
|
+
|
|
126
|
+
if (prefix === "") {
|
|
127
|
+
return `${suffix || "/"}${queryPart}`;
|
|
128
|
+
}
|
|
129
|
+
return `${prefix}${suffix}${queryPart}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
type ParentFetcher = (
|
|
120
133
|
request: string,
|
|
121
134
|
init?: RequestInit,
|
|
@@ -137,8 +150,7 @@ function createMountedFetcher<
|
|
|
137
150
|
? normalized
|
|
138
151
|
: substitutePathParams(normalized, mountParams as Record<string, string>);
|
|
139
152
|
return honoFetcher<ClientAppForMount<T, M>>((request, init) => {
|
|
140
|
-
const
|
|
141
|
-
const url = prefix === "" ? path : `${prefix}${path}`;
|
|
153
|
+
const url = joinMountedRequest(prefix, request);
|
|
142
154
|
return parentFetcher(url, init) as ReturnType<
|
|
143
155
|
ClientAppForMount<T, M>["request"]
|
|
144
156
|
>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/honoFetcherMounted.ts"],"names":["HonoClass"],"mappings":";;;AAmGA,SAAS,mBAAmB,SAAA,EAA2B;AACtD,EAAA,MAAM,OAAA,GAAU,UAAU,IAAA,EAAK;AAC/B,EAAA,IAAI,OAAA,KAAY,EAAA,IAAM,OAAA,KAAY,GAAA,EAAK;AACtC,IAAA,OAAO,EAAA;AAAA,EACR;AACA,EAAA,MAAM,cAAc,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,GAAI,OAAA,GAAU,IAAI,OAAO,CAAA,CAAA;AACnE,EAAA,OAAO,WAAA,CAAY,SAAS,GAAG,CAAA,GAAI,YAAY,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,WAAA;AAC/D;AAEA,SAAS,oBAAA,CACR,MACA,MAAA,EACS;AACT,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAA;AAAA,IAC7B,CAAC,GAAA,EAAK,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM,GAAA,CAAI,OAAA,CAAQ,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,IACnD;AAAA,GACD;AACD;AAOA,SAAS,oBAAA,CAIR,aAAA,EACA,SAAA,EACA,WAAA,EAGC;AACD,EAAA,MAAM,UAAA,GAAa,mBAAmB,SAAS,CAAA;AAC/C,EAAA,MAAM,SACL,WAAA,KAAgB,MAAA,GACb,UAAA,GACA,oBAAA,CAAqB,YAAY,WAAqC,CAAA;AAC1E,EAAA,OAAO,WAAA,CAAqC,CAAC,OAAA,EAAS,IAAA,KAAS;AAC9D,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,GAAI,OAAA,GAAU,IAAI,OAAO,CAAA,CAAA;AAC5D,IAAA,MAAM,MAAM,MAAA,KAAW,EAAA,GAAK,OAAO,CAAA,EAAG,MAAM,GAAG,IAAI,CAAA,CAAA;AACnD,IAAA,OAAO,aAAA,CAAc,KAAK,IAAI,CAAA;AAAA,EAG/B,CAAC,CAAA;AAGF;AAEA,SAAS,UAAU,KAAA,EAA4C;AAC9D,EAAA,OAAO,KAAA,YAAiBA,IAAA;AACzB;AA2DO,SAAS,kBAAA,CAIf,YAAA,EACA,SAAA,EACA,WAAA,EAC8D;AAC9D,EAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,YAAY,CAAA,GACzC,CAAC,GAAA,EAAa,IAAA,KAAuB,YAAA,CAAa,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA,GACnE,YAAA;AACH,EAAA,OAAO,oBAAA;AAAA,IACN,aAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACD;AACD","file":"chunk-2V3BY5GN.js","sourcesContent":["import type { Hono } from \"hono\";\nimport { Hono as HonoClass } from \"hono\";\nimport type { Env, Schema } from \"hono/types\";\nimport type { ExtractSchema } from \"hono/types\";\nimport {\n\thonoFetcher,\n\ttype ParsePathParams,\n\ttype TypedHonoFetcher,\n} from \"./honoFetcher\";\n\ntype SchemaRouteKeys<T extends Hono> = string & keyof ExtractSchema<T>;\n\n/**\n * Client-side view of a Hono app: same routes/schema as the server app but without\n * server `Bindings` (service-binding and browser clients do not send Worker env).\n *\n * Use with a **sub-app** type when the mount path is only on the worker (e.g. mount\n * `/admin` but routes on the type are `/users`). For typed mount prefixes derived from\n * full worker paths, pass the **worker app** type and use {@link MountedClientApp}.\n */\nexport type HonoClientApp<T extends Hono> =\n\tT extends Hono<\n\t\tinfer E extends Env,\n\t\tinfer S extends Schema,\n\t\tinfer BasePath extends string\n\t>\n\t\t? Hono<Omit<E, \"Bindings\">, S, BasePath>\n\t\t: never;\n\ntype HasMorePathSegments<Rest extends string> = Rest extends\n\t| `${string}/${string}`\n\t| `:${string}/${string}`\n\t? true\n\t: false;\n\ntype MountPrefixesWithPrefix<\n\tPrefix extends string,\n\tRoute extends string,\n> = Route extends `/${infer Seg}/${infer Rest}`\n\t? HasMorePathSegments<Rest> extends true\n\t\t?\n\t\t\t\t| `${Prefix}/${Seg}`\n\t\t\t\t| MountPrefixesWithPrefix<`${Prefix}/${Seg}`, `/${Rest}`>\n\t\t: `${Prefix}/${Seg}`\n\t: never;\n\n/** Every mount prefix along a route (`/a/b/c` → `/a` | `/a/b`). */\ntype MountPrefixesOfRoute<Route extends string> =\n\tRoute extends `/${infer Seg}/${infer Rest}`\n\t\t? HasMorePathSegments<Rest> extends true\n\t\t\t? `/${Seg}` | MountPrefixesWithPrefix<`/${Seg}`, `/${Rest}`>\n\t\t\t: `/${Seg}`\n\t\t: never;\n\n/**\n * Mount prefix valid when the worker app schema has routes under `${M}/…`.\n * Includes multi-segment prefixes (`/nested/deep`) and param segments (`/level1/:param`).\n * Excludes top-level-only routes (`/users`, `/x`) — use {@link honoFetcher} for those.\n */\nexport type ValidMountPrefix<T extends Hono> = MountPrefixesOfRoute<\n\tSchemaRouteKeys<T>\n>;\n\n/** Path params required when the mount path contains `:param` segments. */\nexport type MountPathParams<M extends string> = ParsePathParams<M>;\n\ntype StripMountRoute<\n\tRoute extends string,\n\tMount extends string,\n> = Route extends `${Mount}/${infer Rest}` ? `/${Rest}` : never;\n\ntype MountedSchema<T extends Hono, Mount extends string> = {\n\t[Route in SchemaRouteKeys<T> as StripMountRoute<\n\t\tRoute,\n\t\tMount\n\t> extends infer Stripped extends string\n\t\t? Stripped\n\t\t: never]: ExtractSchema<T>[Route];\n} & Schema;\n\n/**\n * Client view of routes under `Mount` on a **worker app** whose schema keys are full\n * paths (e.g. `/admin/users` → client `url: \"/users\"` when `Mount` is `\"/admin\"`).\n */\nexport type MountedClientApp<T extends Hono, Mount extends string> =\n\tT extends Hono<\n\t\tinfer E extends Env,\n\t\tinfer _S extends Schema,\n\t\tinfer BasePath extends string\n\t>\n\t\t? Hono<Omit<E, \"Bindings\">, MountedSchema<T, Mount>, BasePath>\n\t\t: never;\n\ntype ClientAppForMount<T extends Hono, M extends string> =\n\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>;\n\ntype MountParamsArg<M extends string> =\n\tParsePathParams<M> extends never ? undefined : ParsePathParams<M>;\n\nfunction normalizeMountPath(mountPath: string): string {\n\tconst trimmed = mountPath.trim();\n\tif (trimmed === \"\" || trimmed === \"/\") {\n\t\treturn \"\";\n\t}\n\tconst withLeading = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n\treturn withLeading.endsWith(\"/\") ? withLeading.slice(0, -1) : withLeading;\n}\n\nfunction substitutePathParams(\n\tpath: string,\n\tparams: Record<string, string>,\n): string {\n\treturn Object.entries(params).reduce(\n\t\t(acc, [key, value]) => acc.replace(`:${key}`, value),\n\t\tpath,\n\t);\n}\n\ntype ParentFetcher = (\n\trequest: string,\n\tinit?: RequestInit,\n) => Response | Promise<Response>;\n\nfunction createMountedFetcher<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T> | string,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n\tmountParams?: MountParamsArg<M>,\n): TypedHonoFetcher<\n\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>\n> {\n\tconst normalized = normalizeMountPath(mountPath);\n\tconst prefix =\n\t\tmountParams === undefined\n\t\t\t? normalized\n\t\t\t: substitutePathParams(normalized, mountParams as Record<string, string>);\n\treturn honoFetcher<ClientAppForMount<T, M>>((request, init) => {\n\t\tconst path = request.startsWith(\"/\") ? request : `/${request}`;\n\t\tconst url = prefix === \"\" ? path : `${prefix}${path}`;\n\t\treturn parentFetcher(url, init) as ReturnType<\n\t\t\tClientAppForMount<T, M>[\"request\"]\n\t\t>;\n\t}) as TypedHonoFetcher<\n\t\tM extends ValidMountPrefix<T> ? MountedClientApp<T, M> : HonoClientApp<T>\n\t>;\n}\n\nfunction isHonoApp(value: ParentFetcher | Hono): value is Hono {\n\treturn value instanceof HonoClass;\n}\n\n/**\n * Typed client for routes under `mountPath`, using `app.request` as transport.\n * Infers both the app schema and (when valid) stripped mount paths — no type args needed.\n *\n * When `mountPath` contains `:param` segments, pass `mountParams` (same shape as route\n * `params` on {@link honoFetcher}).\n */\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tapp: T,\n\tmountPath: M,\n\tmountParams: MountPathParams<M>,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(app: T, mountPath: M): TypedHonoFetcher<MountedClientApp<T, M>>;\n\n/**\n * Typed client for routes under `mountPath` on a parent fetcher.\n *\n * Use when transport is not `app.request` (service bindings, DO stubs, browser `fetch`, etc.).\n *\n * - **Worker app type** (schema keys include the mount, e.g. `/admin/users`): pass\n * `typeof workerApp` and a {@link ValidMountPrefix}; client `url` values are paths\n * **after** the mount (`/users`, not `/admin/users`). Prefer {@link honoFetcherMounted}\n * with the app instance when using `app.request`.\n * - **Sub-app type** (routes are `/users` on the sub-app, mount is only on the worker):\n * pass `typeof adminRoutes` and the worker mount string (`\"/admin\"`); client URLs\n * match the sub-app schema.\n */\nexport function honoFetcherMounted<T extends Hono>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: string,\n\tmountParams?: Record<string, string>,\n): TypedHonoFetcher<HonoClientApp<T>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n\tmountParams: MountPathParams<M>,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T>,\n>(\n\tparentFetcher: ParentFetcher,\n\tmountPath: M,\n): TypedHonoFetcher<MountedClientApp<T, M>>;\n\nexport function honoFetcherMounted<\n\tT extends Hono,\n\tconst M extends ValidMountPrefix<T> | string,\n>(\n\tappOrFetcher: T | ParentFetcher,\n\tmountPath: M,\n\tmountParams?: MountParamsArg<M>,\n): TypedHonoFetcher<HonoClientApp<T> | MountedClientApp<T, M>> {\n\tconst parentFetcher = isHonoApp(appOrFetcher)\n\t\t? (url: string, init?: RequestInit) => appOrFetcher.request(url, init)\n\t\t: appOrFetcher;\n\treturn createMountedFetcher<T, M>(\n\t\tparentFetcher,\n\t\tmountPath,\n\t\tmountParams,\n\t) as TypedHonoFetcher<HonoClientApp<T> | MountedClientApp<T, M>>;\n}\n"]}
|