better-call 1.0.29 → 1.1.0-beta.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.
package/README.md CHANGED
@@ -412,6 +412,60 @@ Bun.serve({
412
412
 
413
413
  Behind the scenes, the router uses [rou3](https://github.com/unjs/rou3) to match the endpoints and invoke the correct endpoint. You can look at the [rou3 documentation](https://github.com/unjs/rou3) for more information.
414
414
 
415
+ #### Virtual endpoints
416
+
417
+ You can create virtual endpoints by completely omitting the `path`. Virtual endpoints do not get exposed for routing, do not generate OpenAPI docs and cannot be inferred through the [RPC client](#rpc-client), but they can still be invoked directly:
418
+
419
+ ```ts
420
+ import { createEndpoint, createRouter } from "better-call";
421
+
422
+ const endpoint = createEndpoint({
423
+ method: "GET",
424
+ }, async (ctx) => {
425
+ return "ok";
426
+ })
427
+
428
+ const response = await endpoint(); // this works
429
+
430
+ const router = createRouter({ endpoint })
431
+
432
+ Bun.serve({
433
+ fetch: router.handler // endpoint won't be routed through the router handler
434
+ });
435
+
436
+ ```
437
+
438
+ #### Scoped endpoints
439
+
440
+ You can also create endpoints that are exposed for routing, but that cannot be inferred through the client by using the `metadata.scope` option:
441
+
442
+ - `rpc` - the endpoint is exposed to the router, can be invoked directly and is available to the [RPC client](#rpc-client)
443
+ - `server` - the endpoint is exposed to the router, can be invoked directly, but is not available to the client
444
+ - `http` - the endpoint is only exposed to the router
445
+
446
+ ```ts
447
+ import { createEndpoint, createRouter } from "better-call";
448
+
449
+ const endpoint = createEndpoint("/item", {
450
+ method: "GET",
451
+ metadata: {
452
+ scope: "server"
453
+ },
454
+ }, async (ctx) => {
455
+ return "ok";
456
+ })
457
+
458
+ const response = await endpoint(); // this works
459
+
460
+ const router = createRouter({
461
+ endpoint
462
+ })
463
+
464
+ Bun.serve({
465
+ fetch: router.handler // endpoint won't be routed through the router handler
466
+ })
467
+ ```
468
+
415
469
  #### Router Options
416
470
 
417
471
  **routerMiddleware:**
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","names":["options"],"sources":["../src/client.ts"],"sourcesContent":["import { type BetterFetchOption, type BetterFetchResponse, createFetch } from \"@better-fetch/fetch\";\nimport type { Router } from \"./router\";\nimport type { HasRequiredKeys, Prettify, UnionToIntersection } from \"./helper\";\nimport type { Endpoint } from \"./endpoint\";\n\ntype HasRequired<\n\tT extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = T[\"body\"] extends object\n\t? HasRequiredKeys<T[\"body\"]> extends true\n\t\t? true\n\t\t: T[\"query\"] extends object\n\t\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t\t? true\n\t\t\t\t: T[\"params\"] extends object\n\t\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t\t: false\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t: T[\"query\"] extends object\n\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t? true\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t\t: T[\"params\"] extends object\n\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t: false;\n\ntype InferContext<T> = T extends (ctx: infer Ctx) => any\n\t? Ctx extends object\n\t\t? Ctx\n\t\t: never\n\t: never;\n\nexport interface ClientOptions extends BetterFetchOption {\n\tbaseURL: string;\n}\n\ntype WithRequired<T, K> = T & {\n\t[P in K extends string ? K : never]-?: T[P extends keyof T ? P : never];\n};\n\ntype WithoutServerOnly<T extends Record<string, Endpoint>> = {\n\t[K in keyof T]: T[K] extends Endpoint<any, infer O>\n\t\t? O extends { metadata: { SERVER_ONLY: true } }\n\t\t\t? never\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RequiredOptionKeys<\n\tC extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = (undefined extends C[\"body\"]\n\t? {}\n\t: {\n\t\t\tbody: true;\n\t\t}) &\n\t(undefined extends C[\"query\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tquery: true;\n\t\t\t}) &\n\t(undefined extends C[\"params\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tparams: true;\n\t\t\t});\n\nexport const createClient = <R extends Router | Router[\"endpoints\"]>(options: ClientOptions) => {\n\tconst fetch = createFetch(options);\n\ttype API = WithoutServerOnly<\n\t\tR extends { endpoints: Record<string, Endpoint> } ? R[\"endpoints\"] : R\n\t>;\n\ttype Options = API extends {\n\t\t[key: string]: infer T;\n\t}\n\t\t? T extends Endpoint\n\t\t\t? {\n\t\t\t\t\t[key in T[\"options\"][\"method\"] extends \"GET\"\n\t\t\t\t\t\t? T[\"path\"]\n\t\t\t\t\t\t: `@${T[\"options\"][\"method\"] extends string ? Lowercase<T[\"options\"][\"method\"]> : never}${T[\"path\"]}`]: T;\n\t\t\t\t}\n\t\t\t: {}\n\t\t: {};\n\n\ttype O = Prettify<UnionToIntersection<Options>>;\n\treturn async <OPT extends O, K extends keyof OPT, C extends InferContext<OPT[K]>>(\n\t\tpath: K,\n\t\t...options: HasRequired<C> extends true\n\t\t\t? [\n\t\t\t\t\tWithRequired<\n\t\t\t\t\t\tBetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>,\n\t\t\t\t\t\tkeyof RequiredOptionKeys<C>\n\t\t\t\t\t>,\n\t\t\t\t]\n\t\t\t: [BetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>?]\n\t): Promise<\n\t\tBetterFetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>\n\t> => {\n\t\treturn (await fetch(path as string, {\n\t\t\t...options[0],\n\t\t})) as any;\n\t};\n};\n"],"mappings":";;;;;AA6EA,MAAa,gBAAwD,YAA2B;CAC/F,MAAM,8CAAoB,QAAQ;AAiBlC,QAAO,OACN,MACA,GAAGA,cAUC;AACJ,SAAQ,MAAM,MAAM,MAAgB,EACnC,GAAGA,UAAQ,IACX,CAAC"}
1
+ {"version":3,"file":"client.cjs","names":["options"],"sources":["../src/client.ts"],"sourcesContent":["import { type BetterFetchOption, type BetterFetchResponse, createFetch } from \"@better-fetch/fetch\";\nimport type { Router } from \"./router\";\nimport type { HasRequiredKeys, Prettify, UnionToIntersection } from \"./helper\";\nimport type { Endpoint } from \"./endpoint\";\n\ntype HasRequired<\n\tT extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = T[\"body\"] extends object\n\t? HasRequiredKeys<T[\"body\"]> extends true\n\t\t? true\n\t\t: T[\"query\"] extends object\n\t\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t\t? true\n\t\t\t\t: T[\"params\"] extends object\n\t\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t\t: false\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t: T[\"query\"] extends object\n\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t? true\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t\t: T[\"params\"] extends object\n\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t: false;\n\ntype InferContext<T> = T extends (ctx: infer Ctx) => any\n\t? Ctx extends object\n\t\t? Ctx\n\t\t: never\n\t: never;\n\nexport interface ClientOptions extends BetterFetchOption {\n\tbaseURL: string;\n}\n\ntype WithRequired<T, K> = T & {\n\t[P in K extends string ? K : never]-?: T[P extends keyof T ? P : never];\n};\n\ntype InferClientRoutes<T extends Record<string, Endpoint>> = {\n\t[K in keyof T]: T[K] extends Endpoint<any, infer O>\n\t\t? O extends\n\t\t\t\t| { metadata: { scope: \"http\" } }\n\t\t\t\t| { metadata: { scope: \"server\" } }\n\t\t\t\t| { metadata: { SERVER_ONLY: true } }\n\t\t\t\t| { metadata: { isAction: false } }\n\t\t\t? never\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RequiredOptionKeys<\n\tC extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = (undefined extends C[\"body\"]\n\t? {}\n\t: {\n\t\t\tbody: true;\n\t\t}) &\n\t(undefined extends C[\"query\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tquery: true;\n\t\t\t}) &\n\t(undefined extends C[\"params\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tparams: true;\n\t\t\t});\n\nexport const createClient = <R extends Router | Router[\"endpoints\"]>(options: ClientOptions) => {\n\tconst fetch = createFetch(options);\n\ttype API = InferClientRoutes<\n\t\tR extends { endpoints: Record<string, Endpoint> } ? R[\"endpoints\"] : R\n\t>;\n\ttype Options = API extends {\n\t\t[key: string]: infer T;\n\t}\n\t\t? T extends Endpoint\n\t\t\t? {\n\t\t\t\t\t[key in T[\"options\"][\"method\"] extends \"GET\"\n\t\t\t\t\t\t? T[\"path\"]\n\t\t\t\t\t\t: `@${T[\"options\"][\"method\"] extends string ? Lowercase<T[\"options\"][\"method\"]> : never}${T[\"path\"]}`]: T;\n\t\t\t\t}\n\t\t\t: {}\n\t\t: {};\n\n\ttype O = Prettify<UnionToIntersection<Options>>;\n\treturn async <OPT extends O, K extends keyof OPT, C extends InferContext<OPT[K]>>(\n\t\tpath: K,\n\t\t...options: HasRequired<C> extends true\n\t\t\t? [\n\t\t\t\t\tWithRequired<\n\t\t\t\t\t\tBetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>,\n\t\t\t\t\t\tkeyof RequiredOptionKeys<C>\n\t\t\t\t\t>,\n\t\t\t\t]\n\t\t\t: [BetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>?]\n\t): Promise<\n\t\tBetterFetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>\n\t> => {\n\t\treturn (await fetch(path as string, {\n\t\t\t...options[0],\n\t\t})) as any;\n\t};\n};\n"],"mappings":";;;;;AAiFA,MAAa,gBAAwD,YAA2B;CAC/F,MAAM,8CAAoB,QAAQ;AAiBlC,QAAO,OACN,MACA,GAAGA,cAUC;AACJ,SAAQ,MAAM,MAAM,MAAgB,EACnC,GAAGA,UAAQ,IACX,CAAC"}
package/dist/client.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Endpoint, HasRequiredKeys, Router, UnionToIntersection } from "./router-rGV6mTr8.cjs";
1
+ import { Endpoint, HasRequiredKeys, Router, UnionToIntersection } from "./router-DxkLDx_d.cjs";
2
2
  import { BetterFetchOption, BetterFetchResponse } from "@better-fetch/fetch";
3
3
 
4
4
  //#region src/client.d.ts
@@ -12,10 +12,22 @@ interface ClientOptions extends BetterFetchOption {
12
12
  baseURL: string;
13
13
  }
14
14
  type WithRequired<T, K> = T & { [P in K extends string ? K : never]-?: T[P extends keyof T ? P : never] };
15
- type WithoutServerOnly<T extends Record<string, Endpoint>> = { [K in keyof T]: T[K] extends Endpoint<any, infer O> ? O extends {
15
+ type InferClientRoutes<T extends Record<string, Endpoint>> = { [K in keyof T]: T[K] extends Endpoint<any, infer O> ? O extends {
16
+ metadata: {
17
+ scope: "http";
18
+ };
19
+ } | {
20
+ metadata: {
21
+ scope: "server";
22
+ };
23
+ } | {
16
24
  metadata: {
17
25
  SERVER_ONLY: true;
18
26
  };
27
+ } | {
28
+ metadata: {
29
+ isAction: false;
30
+ };
19
31
  } ? never : T[K] : T[K] };
20
32
  type RequiredOptionKeys<C extends {
21
33
  body?: any;
@@ -28,11 +40,11 @@ type RequiredOptionKeys<C extends {
28
40
  }) & (undefined extends C["params"] ? {} : {
29
41
  params: true;
30
42
  });
31
- declare const createClient: <R extends Router | Router["endpoints"]>(options: ClientOptions) => <OPT extends (UnionToIntersection<WithoutServerOnly<R extends {
43
+ declare const createClient: <R extends Router | Router["endpoints"]>(options: ClientOptions) => <OPT extends (UnionToIntersection<InferClientRoutes<R extends {
32
44
  endpoints: Record<string, Endpoint>;
33
45
  } ? R["endpoints"] : R> extends {
34
46
  [key: string]: infer T_1;
35
- } ? T_1 extends Endpoint ? { [key in T_1["options"]["method"] extends "GET" ? T_1["path"] : `@${T_1["options"]["method"] extends string ? Lowercase<T_1["options"]["method"]> : never}${T_1["path"]}`]: T_1 } : {} : {}> extends infer T ? { [K_1 in keyof T]: UnionToIntersection<WithoutServerOnly<R extends {
47
+ } ? T_1 extends Endpoint ? { [key in T_1["options"]["method"] extends "GET" ? T_1["path"] : `@${T_1["options"]["method"] extends string ? Lowercase<T_1["options"]["method"]> : never}${T_1["path"]}`]: T_1 } : {} : {}> extends infer T ? { [K_1 in keyof T]: UnionToIntersection<InferClientRoutes<R extends {
36
48
  endpoints: Record<string, Endpoint>;
37
49
  } ? R["endpoints"] : R> extends {
38
50
  [key: string]: infer T_1;
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Endpoint, HasRequiredKeys, Router, UnionToIntersection } from "./router-NaFkuy-s.js";
1
+ import { Endpoint, HasRequiredKeys, Router, UnionToIntersection } from "./router-DgnLO11b.js";
2
2
  import { BetterFetchOption, BetterFetchResponse } from "@better-fetch/fetch";
3
3
 
4
4
  //#region src/client.d.ts
@@ -12,10 +12,22 @@ interface ClientOptions extends BetterFetchOption {
12
12
  baseURL: string;
13
13
  }
14
14
  type WithRequired<T, K> = T & { [P in K extends string ? K : never]-?: T[P extends keyof T ? P : never] };
15
- type WithoutServerOnly<T extends Record<string, Endpoint>> = { [K in keyof T]: T[K] extends Endpoint<any, infer O> ? O extends {
15
+ type InferClientRoutes<T extends Record<string, Endpoint>> = { [K in keyof T]: T[K] extends Endpoint<any, infer O> ? O extends {
16
+ metadata: {
17
+ scope: "http";
18
+ };
19
+ } | {
20
+ metadata: {
21
+ scope: "server";
22
+ };
23
+ } | {
16
24
  metadata: {
17
25
  SERVER_ONLY: true;
18
26
  };
27
+ } | {
28
+ metadata: {
29
+ isAction: false;
30
+ };
19
31
  } ? never : T[K] : T[K] };
20
32
  type RequiredOptionKeys<C extends {
21
33
  body?: any;
@@ -28,11 +40,11 @@ type RequiredOptionKeys<C extends {
28
40
  }) & (undefined extends C["params"] ? {} : {
29
41
  params: true;
30
42
  });
31
- declare const createClient: <R extends Router | Router["endpoints"]>(options: ClientOptions) => <OPT extends (UnionToIntersection<WithoutServerOnly<R extends {
43
+ declare const createClient: <R extends Router | Router["endpoints"]>(options: ClientOptions) => <OPT extends (UnionToIntersection<InferClientRoutes<R extends {
32
44
  endpoints: Record<string, Endpoint>;
33
45
  } ? R["endpoints"] : R> extends {
34
46
  [key: string]: infer T_1;
35
- } ? T_1 extends Endpoint ? { [key in T_1["options"]["method"] extends "GET" ? T_1["path"] : `@${T_1["options"]["method"] extends string ? Lowercase<T_1["options"]["method"]> : never}${T_1["path"]}`]: T_1 } : {} : {}> extends infer T ? { [K_1 in keyof T]: UnionToIntersection<WithoutServerOnly<R extends {
47
+ } ? T_1 extends Endpoint ? { [key in T_1["options"]["method"] extends "GET" ? T_1["path"] : `@${T_1["options"]["method"] extends string ? Lowercase<T_1["options"]["method"]> : never}${T_1["path"]}`]: T_1 } : {} : {}> extends infer T ? { [K_1 in keyof T]: UnionToIntersection<InferClientRoutes<R extends {
36
48
  endpoints: Record<string, Endpoint>;
37
49
  } ? R["endpoints"] : R> extends {
38
50
  [key: string]: infer T_1;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["options"],"sources":["../src/client.ts"],"sourcesContent":["import { type BetterFetchOption, type BetterFetchResponse, createFetch } from \"@better-fetch/fetch\";\nimport type { Router } from \"./router\";\nimport type { HasRequiredKeys, Prettify, UnionToIntersection } from \"./helper\";\nimport type { Endpoint } from \"./endpoint\";\n\ntype HasRequired<\n\tT extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = T[\"body\"] extends object\n\t? HasRequiredKeys<T[\"body\"]> extends true\n\t\t? true\n\t\t: T[\"query\"] extends object\n\t\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t\t? true\n\t\t\t\t: T[\"params\"] extends object\n\t\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t\t: false\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t: T[\"query\"] extends object\n\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t? true\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t\t: T[\"params\"] extends object\n\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t: false;\n\ntype InferContext<T> = T extends (ctx: infer Ctx) => any\n\t? Ctx extends object\n\t\t? Ctx\n\t\t: never\n\t: never;\n\nexport interface ClientOptions extends BetterFetchOption {\n\tbaseURL: string;\n}\n\ntype WithRequired<T, K> = T & {\n\t[P in K extends string ? K : never]-?: T[P extends keyof T ? P : never];\n};\n\ntype WithoutServerOnly<T extends Record<string, Endpoint>> = {\n\t[K in keyof T]: T[K] extends Endpoint<any, infer O>\n\t\t? O extends { metadata: { SERVER_ONLY: true } }\n\t\t\t? never\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RequiredOptionKeys<\n\tC extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = (undefined extends C[\"body\"]\n\t? {}\n\t: {\n\t\t\tbody: true;\n\t\t}) &\n\t(undefined extends C[\"query\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tquery: true;\n\t\t\t}) &\n\t(undefined extends C[\"params\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tparams: true;\n\t\t\t});\n\nexport const createClient = <R extends Router | Router[\"endpoints\"]>(options: ClientOptions) => {\n\tconst fetch = createFetch(options);\n\ttype API = WithoutServerOnly<\n\t\tR extends { endpoints: Record<string, Endpoint> } ? R[\"endpoints\"] : R\n\t>;\n\ttype Options = API extends {\n\t\t[key: string]: infer T;\n\t}\n\t\t? T extends Endpoint\n\t\t\t? {\n\t\t\t\t\t[key in T[\"options\"][\"method\"] extends \"GET\"\n\t\t\t\t\t\t? T[\"path\"]\n\t\t\t\t\t\t: `@${T[\"options\"][\"method\"] extends string ? Lowercase<T[\"options\"][\"method\"]> : never}${T[\"path\"]}`]: T;\n\t\t\t\t}\n\t\t\t: {}\n\t\t: {};\n\n\ttype O = Prettify<UnionToIntersection<Options>>;\n\treturn async <OPT extends O, K extends keyof OPT, C extends InferContext<OPT[K]>>(\n\t\tpath: K,\n\t\t...options: HasRequired<C> extends true\n\t\t\t? [\n\t\t\t\t\tWithRequired<\n\t\t\t\t\t\tBetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>,\n\t\t\t\t\t\tkeyof RequiredOptionKeys<C>\n\t\t\t\t\t>,\n\t\t\t\t]\n\t\t\t: [BetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>?]\n\t): Promise<\n\t\tBetterFetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>\n\t> => {\n\t\treturn (await fetch(path as string, {\n\t\t\t...options[0],\n\t\t})) as any;\n\t};\n};\n"],"mappings":";;;AA6EA,MAAa,gBAAwD,YAA2B;CAC/F,MAAM,QAAQ,YAAY,QAAQ;AAiBlC,QAAO,OACN,MACA,GAAGA,cAUC;AACJ,SAAQ,MAAM,MAAM,MAAgB,EACnC,GAAGA,UAAQ,IACX,CAAC"}
1
+ {"version":3,"file":"client.js","names":["options"],"sources":["../src/client.ts"],"sourcesContent":["import { type BetterFetchOption, type BetterFetchResponse, createFetch } from \"@better-fetch/fetch\";\nimport type { Router } from \"./router\";\nimport type { HasRequiredKeys, Prettify, UnionToIntersection } from \"./helper\";\nimport type { Endpoint } from \"./endpoint\";\n\ntype HasRequired<\n\tT extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = T[\"body\"] extends object\n\t? HasRequiredKeys<T[\"body\"]> extends true\n\t\t? true\n\t\t: T[\"query\"] extends object\n\t\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t\t? true\n\t\t\t\t: T[\"params\"] extends object\n\t\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t\t: false\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t: T[\"query\"] extends object\n\t\t? HasRequiredKeys<T[\"query\"]> extends true\n\t\t\t? true\n\t\t\t: T[\"params\"] extends object\n\t\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t\t: false\n\t\t: T[\"params\"] extends object\n\t\t\t? HasRequiredKeys<T[\"params\"]>\n\t\t\t: false;\n\ntype InferContext<T> = T extends (ctx: infer Ctx) => any\n\t? Ctx extends object\n\t\t? Ctx\n\t\t: never\n\t: never;\n\nexport interface ClientOptions extends BetterFetchOption {\n\tbaseURL: string;\n}\n\ntype WithRequired<T, K> = T & {\n\t[P in K extends string ? K : never]-?: T[P extends keyof T ? P : never];\n};\n\ntype InferClientRoutes<T extends Record<string, Endpoint>> = {\n\t[K in keyof T]: T[K] extends Endpoint<any, infer O>\n\t\t? O extends\n\t\t\t\t| { metadata: { scope: \"http\" } }\n\t\t\t\t| { metadata: { scope: \"server\" } }\n\t\t\t\t| { metadata: { SERVER_ONLY: true } }\n\t\t\t\t| { metadata: { isAction: false } }\n\t\t\t? never\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RequiredOptionKeys<\n\tC extends {\n\t\tbody?: any;\n\t\tquery?: any;\n\t\tparams?: any;\n\t},\n> = (undefined extends C[\"body\"]\n\t? {}\n\t: {\n\t\t\tbody: true;\n\t\t}) &\n\t(undefined extends C[\"query\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tquery: true;\n\t\t\t}) &\n\t(undefined extends C[\"params\"]\n\t\t? {}\n\t\t: {\n\t\t\t\tparams: true;\n\t\t\t});\n\nexport const createClient = <R extends Router | Router[\"endpoints\"]>(options: ClientOptions) => {\n\tconst fetch = createFetch(options);\n\ttype API = InferClientRoutes<\n\t\tR extends { endpoints: Record<string, Endpoint> } ? R[\"endpoints\"] : R\n\t>;\n\ttype Options = API extends {\n\t\t[key: string]: infer T;\n\t}\n\t\t? T extends Endpoint\n\t\t\t? {\n\t\t\t\t\t[key in T[\"options\"][\"method\"] extends \"GET\"\n\t\t\t\t\t\t? T[\"path\"]\n\t\t\t\t\t\t: `@${T[\"options\"][\"method\"] extends string ? Lowercase<T[\"options\"][\"method\"]> : never}${T[\"path\"]}`]: T;\n\t\t\t\t}\n\t\t\t: {}\n\t\t: {};\n\n\ttype O = Prettify<UnionToIntersection<Options>>;\n\treturn async <OPT extends O, K extends keyof OPT, C extends InferContext<OPT[K]>>(\n\t\tpath: K,\n\t\t...options: HasRequired<C> extends true\n\t\t\t? [\n\t\t\t\t\tWithRequired<\n\t\t\t\t\t\tBetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>,\n\t\t\t\t\t\tkeyof RequiredOptionKeys<C>\n\t\t\t\t\t>,\n\t\t\t\t]\n\t\t\t: [BetterFetchOption<C[\"body\"], C[\"query\"], C[\"params\"]>?]\n\t): Promise<\n\t\tBetterFetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>\n\t> => {\n\t\treturn (await fetch(path as string, {\n\t\t\t...options[0],\n\t\t})) as any;\n\t};\n};\n"],"mappings":";;;AAiFA,MAAa,gBAAwD,YAA2B;CAC/F,MAAM,QAAQ,YAAY,QAAQ;AAiBlC,QAAO,OACN,MACA,GAAGA,cAUC;AACJ,SAAQ,MAAM,MAAM,MAAgB,EACnC,GAAGA,UAAQ,IACX,CAAC"}
package/dist/index.cjs CHANGED
@@ -449,7 +449,7 @@ const createInternalContext = async (context, { options, path }) => {
449
449
  ...context,
450
450
  body: data.body,
451
451
  query: data.query,
452
- path: context.path || path,
452
+ path: context.path || path || "virtual:",
453
453
  context: "context" in context && context.context ? context.context : {},
454
454
  returned: void 0,
455
455
  headers: context?.headers,
@@ -532,7 +532,10 @@ const createInternalContext = async (context, { options, path }) => {
532
532
 
533
533
  //#endregion
534
534
  //#region src/endpoint.ts
535
- const createEndpoint = (path, options, handler) => {
535
+ function createEndpoint(pathOrOptions, handlerOrOptions, handlerOrNever) {
536
+ const path = typeof pathOrOptions === "string" ? pathOrOptions : void 0;
537
+ const options = typeof handlerOrOptions === "object" ? handlerOrOptions : pathOrOptions;
538
+ const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
536
539
  if ((options.method === "GET" || options.method === "HEAD") && options.body) throw new BetterCallError("Body is not allowed with GET or HEAD methods");
537
540
  const internalHandler = async (...inputCtx) => {
538
541
  const context = inputCtx[0] || {};
@@ -568,7 +571,7 @@ const createEndpoint = (path, options, handler) => {
568
571
  internalHandler.options = options;
569
572
  internalHandler.path = path;
570
573
  return internalHandler;
571
- };
574
+ }
572
575
  createEndpoint.create = (opts) => {
573
576
  return (path, options, handler) => {
574
577
  return createEndpoint(path, {
@@ -2392,7 +2395,7 @@ async function generator(endpoints, config$1) {
2392
2395
  const components = { schemas: {} };
2393
2396
  Object.entries(endpoints).forEach(([_, value]) => {
2394
2397
  const options = value.options;
2395
- if (options.metadata?.SERVER_ONLY) return;
2398
+ if (!value.path || options.metadata?.SERVER_ONLY) return;
2396
2399
  if (options.method === "GET") paths[value.path] = { get: {
2397
2400
  tags: ["Default", ...options.metadata?.openapi?.tags || []],
2398
2401
  description: options.metadata?.openapi?.description,
@@ -2481,7 +2484,7 @@ const createRouter = (endpoints, config$1) => {
2481
2484
  const router = (0, rou3.createRouter)();
2482
2485
  const middlewareRouter = (0, rou3.createRouter)();
2483
2486
  for (const endpoint of Object.values(endpoints)) {
2484
- if (!endpoint.options) continue;
2487
+ if (!endpoint.options || !endpoint.path) continue;
2485
2488
  if (endpoint.options?.metadata?.SERVER_ONLY) continue;
2486
2489
  const methods = Array.isArray(endpoint.options?.method) ? endpoint.options.method : [endpoint.options?.method];
2487
2490
  for (const method of methods) (0, rou3.addRoute)(router, method, endpoint.path, endpoint);