@elysiajs/eden 0.3.0-exp-230224.1831 → 0.3.0-rc.1
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/.eslintrc +23 -0
- package/dist/fetch/index.d.ts +4 -0
- package/dist/fetch/types.d.ts +26 -0
- package/dist/fetch.d.ts +1 -0
- package/dist/fetch.js +1 -0
- package/dist/fetch.mjs +33 -0
- package/dist/fn/index.d.ts +4 -0
- package/dist/fn/types.d.ts +24 -0
- package/dist/fn/utils.d.ts +13 -0
- package/dist/fn.d.ts +1 -0
- package/dist/fn.js +1 -0
- package/dist/fn.mjs +80 -0
- package/dist/index.d.ts +3 -15
- package/dist/index.js +1 -1
- package/dist/index.mjs +7 -197
- package/dist/treaty/index.d.ts +17 -0
- package/dist/treaty/types.d.ts +81 -0
- package/dist/treaty/utils.d.ts +1 -0
- package/dist/treaty.d.ts +1 -0
- package/dist/treaty.js +1 -0
- package/dist/treaty.mjs +117 -0
- package/dist/types.d.ts +18 -81
- package/dist/utils-0d2b8b1a.mjs +8 -0
- package/dist/utils-0d7d9b21.js +1 -0
- package/dist/utils.d.ts +0 -14
- package/package.json +30 -7
- package/src/fetch/index.ts +55 -0
- package/src/fetch/types.ts +53 -0
- package/src/fn/index.ts +53 -0
- package/src/fn/types.ts +45 -0
- package/src/fn/utils.ts +86 -0
- package/src/index.ts +34 -253
- package/src/treaty/index.ts +203 -0
- package/src/treaty/types.ts +195 -0
- package/src/treaty/utils.ts +15 -0
- package/src/types.ts +40 -201
- package/src/utils.ts +0 -105
- package/dist/index.umd.js +0 -1
package/dist/treaty.mjs
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { E as p } from "./utils-0d2b8b1a.mjs";
|
|
2
|
+
const E = (n, e, t) => {
|
|
3
|
+
if (n.endsWith("/") || (n += "/"), e === "index" && (e = ""), !t || !Object.keys(t).length)
|
|
4
|
+
return `${n}${e}`;
|
|
5
|
+
let s = "";
|
|
6
|
+
for (const [i, r] of Object.entries(t))
|
|
7
|
+
s += `${i}=${r}&`;
|
|
8
|
+
return `${n}${e}?${s.slice(0, -1)}`;
|
|
9
|
+
};
|
|
10
|
+
class N {
|
|
11
|
+
constructor(e) {
|
|
12
|
+
this.ws = new WebSocket(e), this.url = e;
|
|
13
|
+
}
|
|
14
|
+
send(e) {
|
|
15
|
+
return Array.isArray(e) ? (e.forEach((t) => this.send(t)), this) : (this.ws.send(
|
|
16
|
+
typeof e == "object" ? JSON.stringify(e) : e.toString()
|
|
17
|
+
), this);
|
|
18
|
+
}
|
|
19
|
+
on(e, t, s) {
|
|
20
|
+
return this.addEventListener(e, t, s);
|
|
21
|
+
}
|
|
22
|
+
off(e, t, s) {
|
|
23
|
+
return this.ws.removeEventListener(e, t, s), this;
|
|
24
|
+
}
|
|
25
|
+
subscribe(e, t) {
|
|
26
|
+
return this.addEventListener("message", e, t);
|
|
27
|
+
}
|
|
28
|
+
addEventListener(e, t, s) {
|
|
29
|
+
return this.ws.addEventListener(
|
|
30
|
+
e,
|
|
31
|
+
(i) => {
|
|
32
|
+
if (e === "message") {
|
|
33
|
+
let r = i.data.toString();
|
|
34
|
+
const o = r.charCodeAt(0);
|
|
35
|
+
if (o === 47 || o === 123)
|
|
36
|
+
try {
|
|
37
|
+
r = JSON.parse(r);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
else
|
|
41
|
+
Number.isNaN(+r) ? r === "true" ? r = !0 : r === "fase" && (r = !1) : r = +r;
|
|
42
|
+
t({
|
|
43
|
+
...i,
|
|
44
|
+
data: r
|
|
45
|
+
});
|
|
46
|
+
} else
|
|
47
|
+
t(i);
|
|
48
|
+
},
|
|
49
|
+
s
|
|
50
|
+
), this;
|
|
51
|
+
}
|
|
52
|
+
removeEventListener(e, t, s) {
|
|
53
|
+
return this.off(e, t, s), this;
|
|
54
|
+
}
|
|
55
|
+
close() {
|
|
56
|
+
return this.ws.close(), this;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const v = (n, e = "", t) => new Proxy(() => {
|
|
60
|
+
}, {
|
|
61
|
+
get(s, i, r) {
|
|
62
|
+
return v(n, `${e}/${i.toString()}`);
|
|
63
|
+
},
|
|
64
|
+
apply(s, i, [
|
|
65
|
+
{ $query: r, $fetch: o, $body: w, ...h } = {
|
|
66
|
+
$fetch: void 0,
|
|
67
|
+
$query: void 0,
|
|
68
|
+
$body: void 0
|
|
69
|
+
}
|
|
70
|
+
] = [{}]) {
|
|
71
|
+
const d = e.lastIndexOf("/"), y = e.slice(d + 1), l = E(n, e.slice(0, d), r);
|
|
72
|
+
if (y === "subscribe")
|
|
73
|
+
return new N(
|
|
74
|
+
l.replace(
|
|
75
|
+
/^([^]+):\/\//,
|
|
76
|
+
l.startsWith("https://") ? "wss://" : "ws://"
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
const f = w ?? (Object.keys(h).length ? h : void 0), g = typeof f == "object";
|
|
80
|
+
return fetch(l, {
|
|
81
|
+
method: y,
|
|
82
|
+
body: g ? JSON.stringify(f) : f,
|
|
83
|
+
// ...config.fetch,
|
|
84
|
+
...o,
|
|
85
|
+
headers: f ? {
|
|
86
|
+
"content-type": g ? "application/json" : "text/plain",
|
|
87
|
+
// ...config.fetch?.headers,
|
|
88
|
+
...o == null ? void 0 : o.headers
|
|
89
|
+
} : void 0
|
|
90
|
+
}).then(async (c) => {
|
|
91
|
+
var b;
|
|
92
|
+
let u;
|
|
93
|
+
switch ((b = c.headers.get("Content-Type")) == null ? void 0 : b.split(";")[0]) {
|
|
94
|
+
case "application/json":
|
|
95
|
+
u = await c.json();
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
u = await c.text().then((a) => Number.isNaN(+a) ? a === "true" ? !0 : a === "false" ? !1 : a : +a);
|
|
99
|
+
}
|
|
100
|
+
return c.status > 300 ? {
|
|
101
|
+
data: u,
|
|
102
|
+
error: new p(c.status, await u)
|
|
103
|
+
} : { data: u, error: null };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}), x = (n, e = {}) => new Proxy(
|
|
107
|
+
{},
|
|
108
|
+
{
|
|
109
|
+
get(t, s) {
|
|
110
|
+
return v(n, s);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
export {
|
|
115
|
+
N as EdenWS,
|
|
116
|
+
x as edenTreaty
|
|
117
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,88 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
declare type Asynctify<T> = T extends infer Fn extends (...args: any) => any ? Promisify<Fn> : T extends Record<string, any> ? {
|
|
7
|
-
[K in keyof T]: EdenFn<T[K]>;
|
|
8
|
-
} : never;
|
|
9
|
-
declare type EdenFn<T> = T extends {
|
|
10
|
-
[EXPOSED]: any;
|
|
11
|
-
value: infer Value;
|
|
12
|
-
} ? Asynctify<Value> : Asynctify<T>;
|
|
13
|
-
declare type CreateEdenFn<Exposed extends Record<string, any>> = EdenFn<Exposed> & {
|
|
14
|
-
$set(config: EdenConfig): void;
|
|
15
|
-
$clone(config?: EdenConfig): CreateEdenFn<Exposed>;
|
|
16
|
-
};
|
|
17
|
-
export declare type Eden<App extends Elysia<any>> = App['meta'] extends {
|
|
18
|
-
[key in typeof SCHEMA]: infer Schema extends Record<string, Record<string, TypedRoute>>;
|
|
19
|
-
} ? UnionToIntersection<CreateEden<Schema>> & {
|
|
20
|
-
$fn: CreateEdenFn<App['meta'][typeof EXPOSED]>;
|
|
21
|
-
} : 'Please install Elysia before using Eden';
|
|
22
|
-
export interface EdenCall {
|
|
23
|
-
[x: string]: any;
|
|
24
|
-
$fetch?: RequestInit;
|
|
25
|
-
$query?: Record<string, string | boolean | number>;
|
|
26
|
-
}
|
|
27
|
-
export declare type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
28
|
-
declare type Prettify<T> = {
|
|
29
|
-
[K in keyof T]: T[K];
|
|
30
|
-
} & {};
|
|
31
|
-
declare type TypedRouteToParams<Route extends TypedRoute> = (Route['body'] extends NonNullable<Route['body']> ? Route['body'] extends Record<any, any> ? Route['body'] : {
|
|
32
|
-
$body: Route['body'];
|
|
33
|
-
} : {}) & (Route['query'] extends NonNullable<Route['query']> ? unknown extends Route['query'] ? {} : {
|
|
34
|
-
$query: Route['query'];
|
|
35
|
-
} : {});
|
|
36
|
-
export declare type CreateEden<Server extends Record<string, Record<string, TypedRoute>>, Path extends string = keyof Server extends string ? keyof Server : never, Full extends string = ''> = Path extends `/${infer Start}` ? CreateEden<Server, Start, Path> : Path extends `${infer A}/${infer B}` ? IsPathParameter<A> extends never ? {
|
|
37
|
-
[key in A]: CreateEden<Server, B, Full>;
|
|
38
|
-
} : Record<string, CreateEden<Server, B, Full>> & Record<`$${A}`, `Expected path parameters ':${A}', replace this with any string`> : // Iterate until last string then catch method
|
|
39
|
-
{
|
|
40
|
-
[key in Path extends '' ? 'index' : Path extends `:${infer params}` ? string : Path | CamelCase<Path>]: Full extends keyof Server ? {
|
|
41
|
-
[key in keyof Server[Full] extends string ? Lowercase<keyof Server[Full]> : keyof Server[Full]]: [
|
|
42
|
-
Server[Full][key extends string ? Uppercase<key> : key]
|
|
43
|
-
] extends [infer Route extends TypedRoute] ? undefined extends Route['body'] ? (params?: {
|
|
44
|
-
$query?: EdenCall['$query'];
|
|
45
|
-
$fetch?: EdenCall['$fetch'];
|
|
46
|
-
}) => Promise<Route['response'] extends {
|
|
47
|
-
200: infer ReturnedType;
|
|
48
|
-
} ? ReturnedType | MapError<Route['response']> : unknown> : (params: Prettify<TypedRouteToParams<Route> & {
|
|
49
|
-
$query?: EdenCall['$query'];
|
|
50
|
-
$fetch?: EdenCall['$fetch'];
|
|
51
|
-
}>) => Promise<Route['response'] extends {
|
|
52
|
-
200: infer ReturnedType;
|
|
53
|
-
} ? ReturnedType | MapError<Route['response']> : unknown> : key extends 'subscribe' ? [
|
|
54
|
-
Server[Full][key],
|
|
55
|
-
Server[Full][key]['query']
|
|
56
|
-
] extends [
|
|
57
|
-
infer Route extends TypedRoute,
|
|
58
|
-
infer Query extends TypedRoute['query']
|
|
59
|
-
] ? unknown extends NonNullable<Query> ? (params?: {
|
|
60
|
-
$query?: EdenCall['$query'];
|
|
61
|
-
}) => EdenWS<Route> : Query extends NonNullable<Query> ? (params: {
|
|
62
|
-
$query: Query;
|
|
63
|
-
}) => EdenWS<Route> : (params?: {
|
|
64
|
-
$query?: EdenCall['$query'];
|
|
65
|
-
}) => EdenWS<Route> : never : never;
|
|
66
|
-
} : never;
|
|
67
|
-
} & (Path extends `:${infer params}` ? Record<`$${params}`, `Expected path parameters ':${params}', replace this with any string`> : {});
|
|
68
|
-
declare type CamelCase<S extends string> = S extends `${infer P1}-${infer P2}${infer P3}` ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}` : Lowercase<S>;
|
|
69
|
-
export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
|
|
70
|
-
data: Data;
|
|
71
|
-
rawData: MessageEvent['data'];
|
|
72
|
-
}
|
|
73
|
-
export declare type EdenWSEvent<K extends keyof WebSocketEventMap, Data = unknown> = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K];
|
|
74
|
-
export interface EdenConfig {
|
|
75
|
-
fn?: string;
|
|
76
|
-
fetch?: Omit<RequestInit, 'body'>;
|
|
77
|
-
}
|
|
78
|
-
declare type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
|
|
79
|
-
declare type Range<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
|
|
80
|
-
declare type ErrorRange = Range<300, 599>;
|
|
81
|
-
declare type MapError<T extends Record<number, unknown>> = [
|
|
1
|
+
import type { EdenFetchError } from './utils';
|
|
2
|
+
type Range<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
|
|
3
|
+
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
|
|
4
|
+
type ErrorRange = Range<300, 599>;
|
|
5
|
+
export type MapError<T extends Record<number, unknown>> = [
|
|
82
6
|
{
|
|
83
7
|
[K in keyof T]-?: K extends ErrorRange ? K : never;
|
|
84
8
|
}[keyof T]
|
|
85
9
|
] extends [infer A extends number] ? {
|
|
86
10
|
[K in A]: EdenFetchError<K, T[K]>;
|
|
87
11
|
}[A] : false;
|
|
12
|
+
export type UnionToIntersect<U> = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never;
|
|
13
|
+
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
14
|
+
export type IsNever<T> = [T] extends [never] ? true : false;
|
|
15
|
+
export type IsUnknown<T> = IsAny<T> extends true ? false : unknown extends T ? true : false;
|
|
16
|
+
export type AnyTypedRoute = {
|
|
17
|
+
body: unknown;
|
|
18
|
+
headers: Record<string, any> | undefined;
|
|
19
|
+
query: Record<string, any> | undefined;
|
|
20
|
+
params: Record<string, any> | undefined;
|
|
21
|
+
response: Record<string, unknown> & {
|
|
22
|
+
'200': unknown;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
88
25
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t extends Error{constructor(r,s){super(),this.status=r,this.value=s}}exports.EdenFetchError=t;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import type { EdenCall, EdenConfig } from './types';
|
|
2
1
|
export declare class EdenFetchError<Status extends number = number, Value = unknown> extends Error {
|
|
3
2
|
status: Status;
|
|
4
3
|
value: Value;
|
|
5
4
|
constructor(status: Status, value: Value);
|
|
6
5
|
}
|
|
7
|
-
export declare const composePath: (domain: string, path: string, query: EdenCall['$query'] | undefined) => string;
|
|
8
|
-
export declare class Signal {
|
|
9
|
-
private url;
|
|
10
|
-
private config;
|
|
11
|
-
private pendings;
|
|
12
|
-
private operation;
|
|
13
|
-
private isFetching;
|
|
14
|
-
private sJson;
|
|
15
|
-
constructor(url: string, config: EdenConfig);
|
|
16
|
-
setConfig(config: EdenConfig): void;
|
|
17
|
-
clone(config?: EdenConfig): Signal;
|
|
18
|
-
run(procedure: string[], params: any): Promise<any>;
|
|
19
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elysiajs/eden",
|
|
3
|
-
"version": "0.3.0-
|
|
3
|
+
"version": "0.3.0-rc.1",
|
|
4
4
|
"description": "Fully type-safe Elysia client",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "saltyAom",
|
|
@@ -14,6 +14,24 @@
|
|
|
14
14
|
"import": "./dist/index.mjs",
|
|
15
15
|
"node": "./dist/index.js",
|
|
16
16
|
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./treaty": {
|
|
19
|
+
"require": "./dist/treaty.js",
|
|
20
|
+
"import": "./dist/treaty.mjs",
|
|
21
|
+
"node": "./dist/treaty.js",
|
|
22
|
+
"default": "./dist/treaty.js"
|
|
23
|
+
},
|
|
24
|
+
"./fetch": {
|
|
25
|
+
"require": "./dist/fetch.js",
|
|
26
|
+
"import": "./dist/fetch.mjs",
|
|
27
|
+
"node": "./dist/fetch.js",
|
|
28
|
+
"default": "./dist/fetch.js"
|
|
29
|
+
},
|
|
30
|
+
"./fn": {
|
|
31
|
+
"require": "./dist/fn.js",
|
|
32
|
+
"import": "./dist/fn.mjs",
|
|
33
|
+
"node": "./dist/fn.js",
|
|
34
|
+
"default": "./dist/fn.js"
|
|
17
35
|
}
|
|
18
36
|
},
|
|
19
37
|
"types": "./src/index.ts",
|
|
@@ -36,16 +54,21 @@
|
|
|
36
54
|
"release": "npm run build && npm run test && npm publish --access public"
|
|
37
55
|
},
|
|
38
56
|
"peerDependencies": {
|
|
39
|
-
"
|
|
57
|
+
"@sinclair/typebox": ">= 0.25.24",
|
|
58
|
+
"elysia": ">= 0.3.0-rc.1"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"@sinclair/typebox": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
40
64
|
},
|
|
41
65
|
"devDependencies": {
|
|
42
66
|
"@elysiajs/cors": "^0.1.0",
|
|
43
|
-
"@
|
|
44
|
-
"@sinclair/typebox": "^0.25.21",
|
|
67
|
+
"@sinclair/typebox": "^0.25.24",
|
|
45
68
|
"@types/node": "^18.11.7",
|
|
46
|
-
"bun-types": "^0.5.
|
|
69
|
+
"bun-types": "^0.5.7",
|
|
70
|
+
"elysia": "rc",
|
|
47
71
|
"eslint": "^8.26.0",
|
|
48
|
-
"elysia": "^0.3.0-exp-230223.1133",
|
|
49
72
|
"rimraf": "^3.0.2",
|
|
50
73
|
"typescript": "^4.8.4",
|
|
51
74
|
"vite": "^4.0.1",
|
|
@@ -54,4 +77,4 @@
|
|
|
54
77
|
"dependencies": {
|
|
55
78
|
"superjson": "^1.12.2"
|
|
56
79
|
}
|
|
57
|
-
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Elysia } from 'elysia'
|
|
2
|
+
|
|
3
|
+
import { EdenFetchError } from '../utils'
|
|
4
|
+
import type { EdenFetch } from './types'
|
|
5
|
+
export type { EdenFetch } from './types'
|
|
6
|
+
|
|
7
|
+
export const edenFetch =
|
|
8
|
+
<App extends Elysia<any>>(
|
|
9
|
+
server: string,
|
|
10
|
+
config?: EdenFetch.Config
|
|
11
|
+
): EdenFetch.Create<App> =>
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
async (endpoint: string, { params, body, ...options } = {}) => {
|
|
14
|
+
if (params)
|
|
15
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
16
|
+
endpoint = endpoint.replace(`:${key}`, value)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const contentType = options.headers?.['Content-Type']
|
|
20
|
+
|
|
21
|
+
if (!contentType || contentType === 'application/json')
|
|
22
|
+
body = JSON.stringify(body)
|
|
23
|
+
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
return fetch(server + endpoint, {
|
|
26
|
+
...options,
|
|
27
|
+
headers: {
|
|
28
|
+
'content-type': 'application/json',
|
|
29
|
+
...options.headers
|
|
30
|
+
},
|
|
31
|
+
body
|
|
32
|
+
}).then(async (res) => {
|
|
33
|
+
let data: Promise<unknown>
|
|
34
|
+
|
|
35
|
+
switch (res.headers.get('Content-Type')?.split(';')[0]) {
|
|
36
|
+
case 'application/json':
|
|
37
|
+
data = res.json()
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
default:
|
|
41
|
+
data = res.text().then((data) => {
|
|
42
|
+
if (!Number.isNaN(+data)) return +data
|
|
43
|
+
if (data === 'true') return true
|
|
44
|
+
if (data === 'false') return false
|
|
45
|
+
|
|
46
|
+
return data
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (res.status > 300)
|
|
51
|
+
return new EdenFetchError(res.status, await data)
|
|
52
|
+
|
|
53
|
+
return data
|
|
54
|
+
})
|
|
55
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Elysia, SCHEMA } from 'elysia'
|
|
2
|
+
import type { EdenFetchError } from '../utils'
|
|
3
|
+
import type { MapError, IsUnknown, IsNever, AnyTypedRoute } from '../types'
|
|
4
|
+
|
|
5
|
+
export namespace EdenFetch {
|
|
6
|
+
export type Create<App extends Elysia<any>> = App['meta'] extends Record<
|
|
7
|
+
typeof SCHEMA,
|
|
8
|
+
infer Schema extends Record<string, any>
|
|
9
|
+
>
|
|
10
|
+
? EdenFetch.Fn<Schema>
|
|
11
|
+
: 'Please install Elysia before using Eden'
|
|
12
|
+
|
|
13
|
+
export interface Config {}
|
|
14
|
+
|
|
15
|
+
export type Fn<Schema extends Record<string, any>> = <
|
|
16
|
+
Endpoint extends keyof Schema,
|
|
17
|
+
Method extends Extract<keyof Schema[Endpoint], string>,
|
|
18
|
+
Route extends Schema[Endpoint][Method]
|
|
19
|
+
>(
|
|
20
|
+
endpoint: Endpoint,
|
|
21
|
+
options: Omit<RequestInit, 'body' | 'method' | 'headers'> &
|
|
22
|
+
('get' extends Method
|
|
23
|
+
? {
|
|
24
|
+
method?: Uppercase<Method>
|
|
25
|
+
}
|
|
26
|
+
: {
|
|
27
|
+
method: Uppercase<Method>
|
|
28
|
+
}) &
|
|
29
|
+
(IsNever<keyof Route['params']> extends true
|
|
30
|
+
? {
|
|
31
|
+
params?: Record<never, string>
|
|
32
|
+
}
|
|
33
|
+
: {
|
|
34
|
+
params: Route['params']
|
|
35
|
+
}) &
|
|
36
|
+
(undefined extends Route['headers']
|
|
37
|
+
? {
|
|
38
|
+
headers?: Record<string, string>
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
headers: Route['headers']
|
|
42
|
+
}) &
|
|
43
|
+
(IsUnknown<Route['body']> extends false
|
|
44
|
+
? { body: Route['body'] }
|
|
45
|
+
: { body?: unknown })
|
|
46
|
+
) =>
|
|
47
|
+
| Promise<Route['response']['200']>
|
|
48
|
+
| (MapError<Route['response']> extends infer Errors
|
|
49
|
+
? IsNever<Errors> extends true
|
|
50
|
+
? EdenFetchError<number, string>
|
|
51
|
+
: Errors
|
|
52
|
+
: never)
|
|
53
|
+
}
|
package/src/fn/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Elysia } from 'elysia'
|
|
2
|
+
|
|
3
|
+
import { Signal } from './utils'
|
|
4
|
+
|
|
5
|
+
import type { EdenFn } from './types'
|
|
6
|
+
export type { EdenFn } from './types'
|
|
7
|
+
|
|
8
|
+
const createProxy = (
|
|
9
|
+
domain: string,
|
|
10
|
+
procedures: string[],
|
|
11
|
+
signal: Signal
|
|
12
|
+
): Record<string, unknown> =>
|
|
13
|
+
new Proxy((..._: any[]) => {}, {
|
|
14
|
+
get(target, key, value) {
|
|
15
|
+
return createProxy(domain, [...procedures, key as string], signal)
|
|
16
|
+
},
|
|
17
|
+
apply(target, _, params) {
|
|
18
|
+
const param = params[0]
|
|
19
|
+
const procedure = procedures[0]
|
|
20
|
+
|
|
21
|
+
if (procedures.length === 1) {
|
|
22
|
+
if (
|
|
23
|
+
procedure in Object.prototype ||
|
|
24
|
+
procedure in Promise.prototype
|
|
25
|
+
)
|
|
26
|
+
return target(...params)
|
|
27
|
+
|
|
28
|
+
switch (procedure) {
|
|
29
|
+
case 'toJSON':
|
|
30
|
+
return target(...params)
|
|
31
|
+
|
|
32
|
+
case '$set':
|
|
33
|
+
return signal.setConfig(param)
|
|
34
|
+
|
|
35
|
+
case '$clone':
|
|
36
|
+
return createProxy(domain, [], signal.clone(param))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return signal.run(procedures, params).then((result) => {
|
|
41
|
+
if (result instanceof Error) throw result
|
|
42
|
+
|
|
43
|
+
return result
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}) as any
|
|
47
|
+
|
|
48
|
+
export const edenFn = <App extends Elysia<any>>(
|
|
49
|
+
domain: string,
|
|
50
|
+
config?: EdenFn.Config
|
|
51
|
+
): EdenFn.Create<App> =>
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
createProxy(domain, [], new Signal(domain, config))
|
package/src/fn/types.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Elysia, EXPOSED } from 'elysia'
|
|
2
|
+
|
|
3
|
+
export namespace EdenFn {
|
|
4
|
+
export type Create<App extends Elysia<any>> = App['meta'] extends Record<
|
|
5
|
+
typeof EXPOSED,
|
|
6
|
+
infer Schema extends Record<string, any>
|
|
7
|
+
>
|
|
8
|
+
? EdenFn.Compose<Schema>
|
|
9
|
+
: 'Please install Elysia before using Eden'
|
|
10
|
+
|
|
11
|
+
export interface Config {}
|
|
12
|
+
|
|
13
|
+
export type Compose<Exposed extends Record<string, any>> = Fn<Exposed> & {
|
|
14
|
+
$set(config: Config): void
|
|
15
|
+
$clone(config?: Config): Compose<Exposed>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Fn<T> = T extends {
|
|
19
|
+
[EXPOSED]: any
|
|
20
|
+
value: infer Value
|
|
21
|
+
}
|
|
22
|
+
? Asynctify<Value>
|
|
23
|
+
: Asynctify<T>
|
|
24
|
+
|
|
25
|
+
export interface Config {
|
|
26
|
+
fn?: string
|
|
27
|
+
fetch?: Omit<RequestInit, 'body'>
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type Promisify<T extends (...args: any[]) => any> = T extends (
|
|
32
|
+
...args: infer Args
|
|
33
|
+
) => infer Return
|
|
34
|
+
? Return extends Promise<any>
|
|
35
|
+
? T
|
|
36
|
+
: (...args: Args) => Promise<Return>
|
|
37
|
+
: never
|
|
38
|
+
|
|
39
|
+
type Asynctify<T> = T extends infer Fn extends (...args: any) => any
|
|
40
|
+
? Promisify<Fn>
|
|
41
|
+
: T extends Record<string, any>
|
|
42
|
+
? {
|
|
43
|
+
[K in keyof T]: EdenFn.Fn<T[K]>
|
|
44
|
+
}
|
|
45
|
+
: never
|
package/src/fn/utils.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { serialize, deserialize } from 'superjson'
|
|
2
|
+
import type { EdenFn } from './types'
|
|
3
|
+
|
|
4
|
+
export class Signal {
|
|
5
|
+
private url: string
|
|
6
|
+
private config: EdenFn.Config
|
|
7
|
+
|
|
8
|
+
private pendings: Array<{ n: string[] } | { n: string[]; p: any }> = []
|
|
9
|
+
private operation: Promise<any[]> | null = null
|
|
10
|
+
private isFetching = false
|
|
11
|
+
|
|
12
|
+
private sJson: Promise<{
|
|
13
|
+
serialize: typeof serialize
|
|
14
|
+
deserialize: typeof deserialize
|
|
15
|
+
}>
|
|
16
|
+
|
|
17
|
+
constructor(url: string, config: EdenFn.Config = {}) {
|
|
18
|
+
this.url = url
|
|
19
|
+
this.config = config
|
|
20
|
+
|
|
21
|
+
this.sJson = import('superjson').then((superJson) => {
|
|
22
|
+
return {
|
|
23
|
+
serialize: superJson.serialize,
|
|
24
|
+
deserialize: superJson.deserialize
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setConfig(config: EdenFn.Config) {
|
|
30
|
+
this.config = config
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clone(config?: EdenFn.Config) {
|
|
34
|
+
return new Signal(this.url, config ?? this.config)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async run(procedure: string[], params: any) {
|
|
38
|
+
const current = +this.pendings.length
|
|
39
|
+
this.pendings.push(
|
|
40
|
+
params !== undefined
|
|
41
|
+
? { n: procedure, p: params }
|
|
42
|
+
: { n: procedure }
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (this.isFetching) return this.operation?.then((x) => x[current])
|
|
46
|
+
this.isFetching = true
|
|
47
|
+
|
|
48
|
+
this.operation = new Promise((resolve) => {
|
|
49
|
+
setTimeout(async () => {
|
|
50
|
+
const requests = [...this.pendings]
|
|
51
|
+
this.pendings = []
|
|
52
|
+
|
|
53
|
+
const { serialize, deserialize } = await this.sJson
|
|
54
|
+
|
|
55
|
+
const results = await fetch(
|
|
56
|
+
`${this.url}${this.config.fn ?? '/~fn'}`,
|
|
57
|
+
{
|
|
58
|
+
method: 'POST',
|
|
59
|
+
...this.config.fetch,
|
|
60
|
+
headers: {
|
|
61
|
+
'content-type': 'elysia/fn',
|
|
62
|
+
...this.config.fetch?.headers
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(serialize(requests))
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if (results.status === 200)
|
|
69
|
+
resolve(results.json().then((x) => deserialize(x as any)))
|
|
70
|
+
else
|
|
71
|
+
resolve(
|
|
72
|
+
Array(requests.length).fill(
|
|
73
|
+
new Error(await results.text())
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
}, 33)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const result = await this.operation.then((results) => results[current])
|
|
80
|
+
|
|
81
|
+
this.operation = null
|
|
82
|
+
this.isFetching = false
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
86
|
+
}
|