@elysiajs/eden 0.2.0 → 0.3.0-beta.0
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/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +153 -62
- package/dist/index.umd.js +1 -1
- package/dist/types.d.ts +54 -23
- package/dist/utils.d.ts +19 -0
- package/package.json +14 -9
- package/src/index.ts +70 -23
- package/src/types.ts +120 -67
- package/src/utils.ts +121 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="bun-types" />
|
|
2
2
|
import type { Elysia, TypedSchema } from 'elysia';
|
|
3
|
-
import type { Eden, EdenWSEvent } from './types';
|
|
3
|
+
import type { Eden, EdenConfig, EdenWSEvent } from './types';
|
|
4
4
|
export declare class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
|
|
5
5
|
ws: WebSocket;
|
|
6
6
|
url: string;
|
|
@@ -12,4 +12,4 @@ export declare class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
|
|
|
12
12
|
removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): this;
|
|
13
13
|
close(): this;
|
|
14
14
|
}
|
|
15
|
-
export declare const eden: <App extends Elysia<any>>(domain: string) => Eden<App>;
|
|
15
|
+
export declare const eden: <App extends Elysia<any>>(domain: string, config?: EdenConfig) => Eden<App>;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class
|
|
1
|
+
"use strict";var O=Object.create;var E=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var J=(s,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of P(t))!F.call(s,r)&&r!==e&&E(s,r,{get:()=>t[r],enumerable:!(n=N(t,r))||n.enumerable});return s};var L=(s,t,e)=>(e=s!=null?O(z(s)):{},J(t||!s||!s.__esModule?E(e,"default",{value:s,enumerable:!0}):e,s));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class A extends Error{constructor(t,e){super(),this.status=t,this.value=e}}const W=s=>s.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`),m=(s,t,e)=>{if(s.endsWith("/")||(s+="/"),t=W(t.replace(/index/g,"")),!e||!Object.keys(e).length)return`${s}${t}`;let n="";for(const[r,i]of Object.entries(e))n+=`${r}=${i}&`;return`${s}${t}?${n.slice(0,-1)}`};class p{constructor(t,e){this.pendings=[],this.operation=null,this.isFetching=!1,this.url=t,this.config=e,this.sJson=import("superjson").then(n=>({serialize:n.serialize,deserialize:n.deserialize}))}setConfig(t){this.config=t}clone(t){return new p(this.url,t??this.config)}async run(t,e){var i;const n=+this.pendings.length;if(this.pendings.push(e!==void 0?{n:t,p:e}:{n:t}),this.isFetching)return(i=this.operation)==null?void 0:i.then(o=>o[n]);this.isFetching=!0,this.operation=new Promise(o=>{setTimeout(async()=>{var l;const a=[...this.pendings];this.pendings=[];const{serialize:g,deserialize:y}=await this.sJson,h=await fetch(`${this.url}${this.config.fn??"/~fn"}`,{method:"POST",...this.config.fetch,headers:{"content-type":"elysia/fn",...(l=this.config.fetch)==null?void 0:l.headers},body:JSON.stringify(g(a))});h.status===200?o(h.json().then(u=>y(u))):o(Array(a.length).fill(new Error(await h.text())))},33)});const r=await this.operation.then(o=>o[n]);return this.operation=null,this.isFetching=!1,r}}class ${constructor(t){this.ws=new WebSocket(t),this.url=t}send(t){return Array.isArray(t)?(t.forEach(e=>this.send(e)),this):(this.ws.send(typeof t=="object"?JSON.stringify(t):t.toString()),this)}on(t,e,n){return this.addEventListener(t,e,n)}off(t,e,n){return this.ws.removeEventListener(t,e,n),this}addEventListener(t,e,n){return this.ws.addEventListener(t,r=>{if(t==="message"){let i=r.data.toString();const o=i.charCodeAt(0);if(o===47||o===123)try{i=JSON.parse(i)}catch{}else Number.isNaN(+i)?i==="true"?i=!0:i==="fase"&&(i=!1):i=+i;e({...r,data:i})}else e(r)},n),this}removeEventListener(t,e,n){return this.off(t,e,n),this}close(){return this.ws.close(),this}}const w=(s,t,e)=>new Proxy((...n)=>{},{get(n,r,i){return w(s,[...t,r],e)},apply(n,r,i){const o=i[0];if(t.length===1){if(t[0]in Object.prototype||t[0]in Promise.prototype)return n(i);switch(t[0]){case"toJSON":return n(i);case"$set":return e.setConfig(o);case"$clone":return w(s,[],e.clone(o))}}return e.run(t,i).then(a=>{if(a instanceof Error)throw a;return a})}}),x=(s,t="",e)=>new Proxy(()=>{},{get(n,r,i){return x(s,`${t}/${r.toString()}`,e)},apply(n,r,[{$query:i,$fetch:o,$body:a,...g}={$fetch:void 0,$query:void 0,$body:void 0}]=[{}]){var b;const y=t.lastIndexOf("/"),h=t.slice(y+1),l=m(s,t.slice(0,y),i);if(h==="subscribe")return new $(l.replace(/^([^]+):\/\//,l.startsWith("https://")?"wss://":"ws://"));const u=a??(Object.keys(g).length?g:void 0),v=typeof u=="object";return fetch(l,{method:h,body:v?JSON.stringify(u):u,...e.fetch,...o,headers:u?{"content-type":v?"application/json":"text/plain",...(b=e.fetch)==null?void 0:b.headers,...o==null?void 0:o.headers}:void 0}).then(async c=>{var S,j;if(c.status>300){let d;if((S=c.headers.get("content-type"))!=null&&S.includes("application/json"))try{d=await c.json()}catch{d=await c.text()}else d=await c.text();return new A(c.status,d)}if((j=c.headers.get("content-type"))!=null&&j.includes("application/json"))try{return await c.json()}catch{}let f=await c.text();return Number.isNaN(+f)?f==="true"?!0:f==="false"?!1:f:+f})}}),C=(s,t={})=>new Proxy({},{get(e,n){return n==="$fn"?w(s,[],new p(s,t)):x(s,n,t)}});exports.EdenWS=$;exports.eden=C;
|
package/dist/index.mjs
CHANGED
|
@@ -1,108 +1,199 @@
|
|
|
1
|
-
class
|
|
2
|
-
constructor(e) {
|
|
3
|
-
this.
|
|
1
|
+
class E extends Error {
|
|
2
|
+
constructor(t, e) {
|
|
3
|
+
super(), this.status = t, this.value = e;
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
}
|
|
6
|
+
const N = (i) => i.replace(/[A-Z]/g, (t) => `-${t.toLowerCase()}`), O = (i, t, e) => {
|
|
7
|
+
if (i.endsWith("/") || (i += "/"), t = N(t.replace(/index/g, "")), !e || !Object.keys(e).length)
|
|
8
|
+
return `${i}${t}`;
|
|
9
|
+
let s = "";
|
|
10
|
+
for (const [o, n] of Object.entries(e))
|
|
11
|
+
s += `${o}=${n}&`;
|
|
12
|
+
return `${i}${t}?${s.slice(0, -1)}`;
|
|
13
|
+
};
|
|
14
|
+
class p {
|
|
15
|
+
constructor(t, e) {
|
|
16
|
+
this.pendings = [], this.operation = null, this.isFetching = !1, this.url = t, this.config = e, this.sJson = import("superjson").then((s) => ({
|
|
17
|
+
serialize: s.serialize,
|
|
18
|
+
deserialize: s.deserialize
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
setConfig(t) {
|
|
22
|
+
this.config = t;
|
|
23
|
+
}
|
|
24
|
+
clone(t) {
|
|
25
|
+
return new p(this.url, t ?? this.config);
|
|
26
|
+
}
|
|
27
|
+
async run(t, e) {
|
|
28
|
+
var n;
|
|
29
|
+
const s = +this.pendings.length;
|
|
30
|
+
if (this.pendings.push(
|
|
31
|
+
e !== void 0 ? { n: t, p: e } : { n: t }
|
|
32
|
+
), this.isFetching)
|
|
33
|
+
return (n = this.operation) == null ? void 0 : n.then((r) => r[s]);
|
|
34
|
+
this.isFetching = !0, this.operation = new Promise((r) => {
|
|
35
|
+
setTimeout(async () => {
|
|
36
|
+
var l;
|
|
37
|
+
const a = [...this.pendings];
|
|
38
|
+
this.pendings = [];
|
|
39
|
+
const { serialize: d, deserialize: y } = await this.sJson, h = await fetch(
|
|
40
|
+
`${this.url}${this.config.fn ?? "/~fn"}`,
|
|
41
|
+
{
|
|
42
|
+
method: "POST",
|
|
43
|
+
...this.config.fetch,
|
|
44
|
+
headers: {
|
|
45
|
+
"content-type": "elysia/fn",
|
|
46
|
+
...(l = this.config.fetch) == null ? void 0 : l.headers
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(d(a))
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
h.status === 200 ? r(h.json().then((u) => y(u))) : r(
|
|
52
|
+
Array(a.length).fill(
|
|
53
|
+
new Error(await h.text())
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
}, 33);
|
|
57
|
+
});
|
|
58
|
+
const o = await this.operation.then((r) => r[s]);
|
|
59
|
+
return this.operation = null, this.isFetching = !1, o;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
class S {
|
|
63
|
+
constructor(t) {
|
|
64
|
+
this.ws = new WebSocket(t), this.url = t;
|
|
65
|
+
}
|
|
66
|
+
send(t) {
|
|
67
|
+
return Array.isArray(t) ? (t.forEach((e) => this.send(e)), this) : (this.ws.send(
|
|
68
|
+
typeof t == "object" ? JSON.stringify(t) : t.toString()
|
|
8
69
|
), this);
|
|
9
70
|
}
|
|
10
|
-
on(
|
|
11
|
-
return this.addEventListener(
|
|
71
|
+
on(t, e, s) {
|
|
72
|
+
return this.addEventListener(t, e, s);
|
|
12
73
|
}
|
|
13
|
-
off(
|
|
14
|
-
return this.ws.removeEventListener(
|
|
74
|
+
off(t, e, s) {
|
|
75
|
+
return this.ws.removeEventListener(t, e, s), this;
|
|
15
76
|
}
|
|
16
|
-
addEventListener(
|
|
77
|
+
addEventListener(t, e, s) {
|
|
17
78
|
return this.ws.addEventListener(
|
|
18
|
-
|
|
19
|
-
(
|
|
20
|
-
if (
|
|
21
|
-
let
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
79
|
+
t,
|
|
80
|
+
(o) => {
|
|
81
|
+
if (t === "message") {
|
|
82
|
+
let n = o.data.toString();
|
|
83
|
+
const r = n.charCodeAt(0);
|
|
84
|
+
if (r === 47 || r === 123)
|
|
24
85
|
try {
|
|
25
|
-
|
|
86
|
+
n = JSON.parse(n);
|
|
26
87
|
} catch {
|
|
27
88
|
}
|
|
28
89
|
else
|
|
29
|
-
Number.isNaN(+
|
|
30
|
-
|
|
31
|
-
...
|
|
32
|
-
data:
|
|
90
|
+
Number.isNaN(+n) ? n === "true" ? n = !0 : n === "fase" && (n = !1) : n = +n;
|
|
91
|
+
e({
|
|
92
|
+
...o,
|
|
93
|
+
data: n
|
|
33
94
|
});
|
|
34
95
|
} else
|
|
35
|
-
|
|
96
|
+
e(o);
|
|
36
97
|
},
|
|
37
98
|
s
|
|
38
99
|
), this;
|
|
39
100
|
}
|
|
40
|
-
removeEventListener(
|
|
41
|
-
return this.off(
|
|
101
|
+
removeEventListener(t, e, s) {
|
|
102
|
+
return this.off(t, e, s), this;
|
|
42
103
|
}
|
|
43
104
|
close() {
|
|
44
105
|
return this.ws.close(), this;
|
|
45
106
|
}
|
|
46
107
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
108
|
+
const w = (i, t, e) => new Proxy((...s) => {
|
|
109
|
+
}, {
|
|
110
|
+
get(s, o, n) {
|
|
111
|
+
return w(i, [...t, o], e);
|
|
112
|
+
},
|
|
113
|
+
apply(s, o, n) {
|
|
114
|
+
const r = n[0];
|
|
115
|
+
if (t.length === 1) {
|
|
116
|
+
if (t[0] in Object.prototype || t[0] in Promise.prototype)
|
|
117
|
+
return s(n);
|
|
118
|
+
switch (t[0]) {
|
|
119
|
+
case "toJSON":
|
|
120
|
+
return s(n);
|
|
121
|
+
case "$set":
|
|
122
|
+
return e.setConfig(r);
|
|
123
|
+
case "$clone":
|
|
124
|
+
return w(i, [], e.clone(r));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return e.run(t, n).then((a) => {
|
|
128
|
+
if (a instanceof Error)
|
|
129
|
+
throw a;
|
|
130
|
+
return a;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}), j = (i, t = "", e) => new Proxy(() => {
|
|
55
134
|
}, {
|
|
56
|
-
get(
|
|
57
|
-
return
|
|
135
|
+
get(s, o, n) {
|
|
136
|
+
return j(i, `${t}/${o.toString()}`, e);
|
|
58
137
|
},
|
|
59
|
-
apply(
|
|
60
|
-
{ $query:
|
|
138
|
+
apply(s, o, [
|
|
139
|
+
{ $query: n, $fetch: r, $body: a, ...d } = {
|
|
61
140
|
$fetch: void 0,
|
|
62
141
|
$query: void 0,
|
|
63
142
|
$body: void 0
|
|
64
143
|
}
|
|
65
144
|
] = [{}]) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
145
|
+
var b;
|
|
146
|
+
const y = t.lastIndexOf("/"), h = t.slice(y + 1), l = O(i, t.slice(0, y), n);
|
|
147
|
+
if (h === "subscribe")
|
|
148
|
+
return new S(
|
|
149
|
+
l.replace(
|
|
70
150
|
/^([^]+):\/\//,
|
|
71
|
-
|
|
151
|
+
l.startsWith("https://") ? "wss://" : "ws://"
|
|
72
152
|
)
|
|
73
153
|
);
|
|
74
|
-
const
|
|
75
|
-
return fetch(
|
|
76
|
-
method:
|
|
77
|
-
body:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
154
|
+
const u = a ?? (Object.keys(d).length ? d : void 0), v = typeof u == "object";
|
|
155
|
+
return fetch(l, {
|
|
156
|
+
method: h,
|
|
157
|
+
body: v ? JSON.stringify(u) : u,
|
|
158
|
+
...e.fetch,
|
|
159
|
+
...r,
|
|
160
|
+
headers: u ? {
|
|
161
|
+
"content-type": v ? "application/json" : "text/plain",
|
|
162
|
+
...(b = e.fetch) == null ? void 0 : b.headers,
|
|
81
163
|
...r == null ? void 0 : r.headers
|
|
82
|
-
} : void 0
|
|
83
|
-
...r
|
|
164
|
+
} : void 0
|
|
84
165
|
}).then(async (c) => {
|
|
85
|
-
var
|
|
86
|
-
if (c.status > 300)
|
|
87
|
-
|
|
88
|
-
|
|
166
|
+
var x, $;
|
|
167
|
+
if (c.status > 300) {
|
|
168
|
+
let g;
|
|
169
|
+
if ((x = c.headers.get("content-type")) != null && x.includes("application/json"))
|
|
170
|
+
try {
|
|
171
|
+
g = await c.json();
|
|
172
|
+
} catch {
|
|
173
|
+
g = await c.text();
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
g = await c.text();
|
|
177
|
+
return new E(c.status, g);
|
|
178
|
+
}
|
|
179
|
+
if (($ = c.headers.get("content-type")) != null && $.includes("application/json"))
|
|
89
180
|
try {
|
|
90
181
|
return await c.json();
|
|
91
182
|
} catch {
|
|
92
183
|
}
|
|
93
|
-
let
|
|
94
|
-
return Number.isNaN(+
|
|
184
|
+
let f = await c.text();
|
|
185
|
+
return Number.isNaN(+f) ? f === "true" ? !0 : f === "false" ? !1 : f : +f;
|
|
95
186
|
});
|
|
96
187
|
}
|
|
97
|
-
}),
|
|
188
|
+
}), z = (i, t = {}) => new Proxy(
|
|
98
189
|
{},
|
|
99
190
|
{
|
|
100
|
-
get(e,
|
|
101
|
-
return
|
|
191
|
+
get(e, s) {
|
|
192
|
+
return s === "$fn" ? w(i, [], new p(i, t)) : j(i, s, t);
|
|
102
193
|
}
|
|
103
194
|
}
|
|
104
195
|
);
|
|
105
196
|
export {
|
|
106
|
-
|
|
107
|
-
|
|
197
|
+
S as EdenWS,
|
|
198
|
+
z as eden
|
|
108
199
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(h,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(h=typeof globalThis<"u"?globalThis:h||self,d(h["@elysia/eden"]={}))})(this,function(h){"use strict";class d extends Error{constructor(t,e){super(),this.status=t,this.value=e}}const N=i=>i.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`),P=(i,t,e)=>{if(i.endsWith("/")||(i+="/"),t=N(t.replace(/index/g,"")),!e||!Object.keys(e).length)return`${i}${t}`;let s="";for(const[o,n]of Object.entries(e))s+=`${o}=${n}&`;return`${i}${t}?${s.slice(0,-1)}`};class v{constructor(t,e){this.pendings=[],this.operation=null,this.isFetching=!1,this.url=t,this.config=e,this.sJson=import("superjson").then(s=>({serialize:s.serialize,deserialize:s.deserialize}))}setConfig(t){this.config=t}clone(t){return new v(this.url,t??this.config)}async run(t,e){var n;const s=+this.pendings.length;if(this.pendings.push(e!==void 0?{n:t,p:e}:{n:t}),this.isFetching)return(n=this.operation)==null?void 0:n.then(r=>r[s]);this.isFetching=!0,this.operation=new Promise(r=>{setTimeout(async()=>{var u;const a=[...this.pendings];this.pendings=[];const{serialize:p,deserialize:w}=await this.sJson,l=await fetch(`${this.url}${this.config.fn??"/~fn"}`,{method:"POST",...this.config.fetch,headers:{"content-type":"elysia/fn",...(u=this.config.fetch)==null?void 0:u.headers},body:JSON.stringify(p(a))});l.status===200?r(l.json().then(f=>w(f))):r(Array(a.length).fill(new Error(await l.text())))},33)});const o=await this.operation.then(r=>r[s]);return this.operation=null,this.isFetching=!1,o}}class j{constructor(t){this.ws=new WebSocket(t),this.url=t}send(t){return Array.isArray(t)?(t.forEach(e=>this.send(e)),this):(this.ws.send(typeof t=="object"?JSON.stringify(t):t.toString()),this)}on(t,e,s){return this.addEventListener(t,e,s)}off(t,e,s){return this.ws.removeEventListener(t,e,s),this}addEventListener(t,e,s){return this.ws.addEventListener(t,o=>{if(t==="message"){let n=o.data.toString();const r=n.charCodeAt(0);if(r===47||r===123)try{n=JSON.parse(n)}catch{}else Number.isNaN(+n)?n==="true"?n=!0:n==="fase"&&(n=!1):n=+n;e({...o,data:n})}else e(o)},s),this}removeEventListener(t,e,s){return this.off(t,e,s),this}close(){return this.ws.close(),this}}const b=(i,t,e)=>new Proxy((...s)=>{},{get(s,o,n){return b(i,[...t,o],e)},apply(s,o,n){const r=n[0];if(t.length===1){if(t[0]in Object.prototype||t[0]in Promise.prototype)return s(n);switch(t[0]){case"toJSON":return s(n);case"$set":return e.setConfig(r);case"$clone":return b(i,[],e.clone(r))}}return e.run(t,n).then(a=>{if(a instanceof Error)throw a;return a})}}),S=(i,t="",e)=>new Proxy(()=>{},{get(s,o,n){return S(i,`${t}/${o.toString()}`,e)},apply(s,o,[{$query:n,$fetch:r,$body:a,...p}={$fetch:void 0,$query:void 0,$body:void 0}]=[{}]){var E;const w=t.lastIndexOf("/"),l=t.slice(w+1),u=P(i,t.slice(0,w),n);if(l==="subscribe")return new j(u.replace(/^([^]+):\/\//,u.startsWith("https://")?"wss://":"ws://"));const f=a??(Object.keys(p).length?p:void 0),x=typeof f=="object";return fetch(u,{method:l,body:x?JSON.stringify(f):f,...e.fetch,...r,headers:f?{"content-type":x?"application/json":"text/plain",...(E=e.fetch)==null?void 0:E.headers,...r==null?void 0:r.headers}:void 0}).then(async c=>{var $,O;if(c.status>300){let y;if(($=c.headers.get("content-type"))!=null&&$.includes("application/json"))try{y=await c.json()}catch{y=await c.text()}else y=await c.text();return new d(c.status,y)}if((O=c.headers.get("content-type"))!=null&&O.includes("application/json"))try{return await c.json()}catch{}let g=await c.text();return Number.isNaN(+g)?g==="true"?!0:g==="false"?!1:g:+g})}}),m=(i,t={})=>new Proxy({},{get(e,s){return s==="$fn"?b(i,[],new v(i,t)):S(i,s,t)}});h.EdenWS=j,h.eden=m,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
|
package/dist/types.d.ts
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
/// <reference types="bun-types" />
|
|
2
|
-
import type { Elysia, SCHEMA, TypedRoute, IsPathParameter } from 'elysia';
|
|
2
|
+
import type { Elysia, SCHEMA, TypedRoute, IsPathParameter, EXPOSED } from 'elysia';
|
|
3
3
|
import type { EdenWS } from '.';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { type EdenFetchError } from './utils';
|
|
5
|
+
declare type Promisify<T extends (...args: any[]) => any> = T extends (...args: infer Args) => infer Return ? Return extends Promise<any> ? T : (...args: Args) => Promise<Return> : never;
|
|
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 {
|
|
6
18
|
[key in typeof SCHEMA]: infer Schema extends Record<string, Record<string, TypedRoute>>;
|
|
7
|
-
} ?
|
|
19
|
+
} ? UnionToIntersection<CreateEden<Schema>> & {
|
|
20
|
+
$fn: CreateEdenFn<App['meta'][typeof EXPOSED]>;
|
|
21
|
+
} : 'Please install Elysia before using Eden';
|
|
8
22
|
export interface EdenCall {
|
|
9
23
|
[x: string]: any;
|
|
10
24
|
$fetch?: RequestInit;
|
|
11
25
|
$query?: Record<string, string | boolean | number>;
|
|
12
26
|
}
|
|
13
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
|
+
} & {};
|
|
14
31
|
declare type TypedRouteToParams<Route extends TypedRoute> = (Route['body'] extends NonNullable<Route['body']> ? Route['body'] extends Record<any, any> ? Route['body'] : {
|
|
15
32
|
$body: Route['body'];
|
|
16
33
|
} : {}) & (Route['query'] extends NonNullable<Route['query']> ? unknown extends Route['query'] ? {} : {
|
|
@@ -21,31 +38,31 @@ export declare type CreateEden<Server extends Record<string, Record<string, Type
|
|
|
21
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
|
|
22
39
|
{
|
|
23
40
|
[key in Path extends '' ? 'index' : Path extends `:${infer params}` ? string : Path | CamelCase<Path>]: Full extends keyof Server ? {
|
|
24
|
-
[key in keyof Server[Full] extends string ? Lowercase<keyof Server[Full]> : keyof Server[Full]]:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
$query: Server[Full][key]['query'];
|
|
28
|
-
}) => EdenWS<Server[Full][key]> : (params?: {
|
|
29
|
-
$query?: EdenCall['$query'];
|
|
30
|
-
}) => EdenWS<Server[Full][key]> : (params?: {
|
|
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?: {
|
|
31
44
|
$query?: EdenCall['$query'];
|
|
32
45
|
$fetch?: EdenCall['$fetch'];
|
|
33
|
-
}) => Promise<
|
|
46
|
+
}) => Promise<Route['response'] extends {
|
|
34
47
|
200: infer ReturnedType;
|
|
35
|
-
} ? ReturnedType
|
|
36
|
-
200: infer ReturnedType;
|
|
37
|
-
} ? ReturnedType : unknown> : key extends 'subscribe' ? unknown extends NonNullable<Server[Full][key]['query']> ? (params?: {
|
|
38
|
-
$query?: EdenCall['$query'];
|
|
39
|
-
}) => EdenWS<Server[Full][key]> : Server[Full][key]['query'] extends NonNullable<Server[Full][key]['query']> ? (params: {
|
|
40
|
-
$query: Server[Full][key]['query'];
|
|
41
|
-
}) => EdenWS<Server[Full][key]> : (params?: {
|
|
42
|
-
$query?: EdenCall['$query'];
|
|
43
|
-
}) => EdenWS<Server[Full][key]> : (params: TypedRouteToParams<Server[Full][key extends string ? Uppercase<key> : key]> & {
|
|
48
|
+
} ? ReturnedType | MapError<Route['response']> : unknown> : (params: Prettify<TypedRouteToParams<Route> & {
|
|
44
49
|
$query?: EdenCall['$query'];
|
|
45
50
|
$fetch?: EdenCall['$fetch'];
|
|
46
|
-
}) => Promise<
|
|
51
|
+
}>) => Promise<Route['response'] extends {
|
|
47
52
|
200: infer ReturnedType;
|
|
48
|
-
} ? ReturnedType : unknown
|
|
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;
|
|
49
66
|
} : never;
|
|
50
67
|
} & (Path extends `:${infer params}` ? Record<`$${params}`, `Expected path parameters ':${params}', replace this with any string`> : {});
|
|
51
68
|
declare type CamelCase<S extends string> = S extends `${infer P1}-${infer P2}${infer P3}` ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}` : Lowercase<S>;
|
|
@@ -54,4 +71,18 @@ export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
|
|
|
54
71
|
rawData: MessageEvent['data'];
|
|
55
72
|
}
|
|
56
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>> = [
|
|
82
|
+
{
|
|
83
|
+
[K in keyof T]-?: K extends ErrorRange ? K : never;
|
|
84
|
+
}[keyof T]
|
|
85
|
+
] extends [infer A extends number] ? {
|
|
86
|
+
[K in A]: EdenFetchError<K, T[K]>;
|
|
87
|
+
}[A] : false;
|
|
57
88
|
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EdenCall, EdenConfig } from './types';
|
|
2
|
+
export declare class EdenFetchError<Status extends number = number, Value = unknown> extends Error {
|
|
3
|
+
status: Status;
|
|
4
|
+
value: Value;
|
|
5
|
+
constructor(status: Status, value: Value);
|
|
6
|
+
}
|
|
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
|
+
"version": "0.3.0-beta.0",
|
|
4
4
|
"description": "Fully type-safe Elysia client",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "saltyAom",
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"main": "./dist/index.js",
|
|
11
11
|
"exports": {
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
".": {
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"node": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
16
18
|
},
|
|
17
19
|
"types": "./src/index.ts",
|
|
18
20
|
"keywords": [
|
|
@@ -34,19 +36,22 @@
|
|
|
34
36
|
"release": "npm run build && npm run test && npm publish --access public"
|
|
35
37
|
},
|
|
36
38
|
"peerDependencies": {
|
|
37
|
-
"elysia": ">= 0.
|
|
39
|
+
"elysia": ">= 0.3.0-beta.0"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@elysiajs/cors": "^0.1.0",
|
|
41
|
-
"@elysiajs/websocket": "^0.
|
|
43
|
+
"@elysiajs/websocket": "^0.3.0-beta.0",
|
|
42
44
|
"@sinclair/typebox": "^0.25.21",
|
|
43
45
|
"@types/node": "^18.11.7",
|
|
44
|
-
"bun-types": "^0.
|
|
46
|
+
"bun-types": "^0.5.0",
|
|
45
47
|
"eslint": "^8.26.0",
|
|
46
|
-
"elysia": "^0.
|
|
48
|
+
"elysia": "^0.3.0-exp-230223.1133",
|
|
47
49
|
"rimraf": "^3.0.2",
|
|
48
50
|
"typescript": "^4.8.4",
|
|
49
51
|
"vite": "^4.0.1",
|
|
50
52
|
"vite-plugin-dts": "^1.7.1"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"superjson": "^1.12.2"
|
|
51
56
|
}
|
|
52
57
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,9 +4,11 @@ import type {
|
|
|
4
4
|
CreateEden,
|
|
5
5
|
Eden,
|
|
6
6
|
EdenCall,
|
|
7
|
+
EdenConfig,
|
|
7
8
|
EdenWSEvent,
|
|
8
9
|
UnionToIntersection
|
|
9
10
|
} from './types'
|
|
11
|
+
import { composePath, EdenFetchError, Signal } from './utils'
|
|
10
12
|
|
|
11
13
|
export class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
|
|
12
14
|
ws: WebSocket
|
|
@@ -102,32 +104,54 @@ export class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
const
|
|
106
|
-
str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
107
|
-
|
|
108
|
-
const composePath = (
|
|
107
|
+
const createFn = (
|
|
109
108
|
domain: string,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
) =>
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
procedure: string[],
|
|
110
|
+
signal: Signal
|
|
111
|
+
): Record<string, unknown> =>
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
new Proxy((...v: any[]) => {}, {
|
|
114
|
+
get(target, key, value) {
|
|
115
|
+
return createFn(domain, [...procedure, key as string], signal)
|
|
116
|
+
},
|
|
117
|
+
apply(target, _, params) {
|
|
118
|
+
const param = params[0]
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
if (procedure.length === 1) {
|
|
121
|
+
if (
|
|
122
|
+
procedure[0] in Object.prototype ||
|
|
123
|
+
procedure[0] in Promise.prototype
|
|
124
|
+
)
|
|
125
|
+
return target(params)
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
switch (procedure[0]) {
|
|
128
|
+
case 'toJSON':
|
|
129
|
+
return target(params)
|
|
120
130
|
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
case '$set':
|
|
132
|
+
return signal.setConfig(param)
|
|
133
|
+
|
|
134
|
+
case '$clone':
|
|
135
|
+
return createFn(domain, [], signal.clone(param))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return signal.run(procedure, params).then((result) => {
|
|
140
|
+
if (result instanceof Error) throw result
|
|
141
|
+
|
|
142
|
+
return result
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
})
|
|
123
146
|
|
|
124
147
|
const createProxy = (
|
|
125
148
|
domain: string,
|
|
126
|
-
path: string = ''
|
|
149
|
+
path: string = '',
|
|
150
|
+
config: EdenConfig
|
|
127
151
|
): Record<string, unknown> =>
|
|
128
152
|
new Proxy(() => {}, {
|
|
129
153
|
get(target, key, value) {
|
|
130
|
-
return createProxy(domain, `${path}/${key.toString()}
|
|
154
|
+
return createProxy(domain, `${path}/${key.toString()}`, config)
|
|
131
155
|
},
|
|
132
156
|
apply(
|
|
133
157
|
target,
|
|
@@ -159,18 +183,35 @@ const createProxy = (
|
|
|
159
183
|
return fetch(url, {
|
|
160
184
|
method,
|
|
161
185
|
body: isObject ? JSON.stringify(body) : body,
|
|
186
|
+
...config.fetch,
|
|
187
|
+
...$fetch,
|
|
162
188
|
headers: body
|
|
163
189
|
? {
|
|
164
190
|
'content-type': isObject
|
|
165
191
|
? 'application/json'
|
|
166
192
|
: 'text/plain',
|
|
167
|
-
|
|
193
|
+
...config.fetch?.headers,
|
|
168
194
|
...$fetch?.['headers']
|
|
169
195
|
}
|
|
170
|
-
: undefined
|
|
171
|
-
...$fetch
|
|
196
|
+
: undefined
|
|
172
197
|
}).then(async (res) => {
|
|
173
|
-
if (res.status > 300)
|
|
198
|
+
if (res.status > 300) {
|
|
199
|
+
let data
|
|
200
|
+
|
|
201
|
+
if (
|
|
202
|
+
res.headers
|
|
203
|
+
.get('content-type')
|
|
204
|
+
?.includes('application/json')
|
|
205
|
+
)
|
|
206
|
+
try {
|
|
207
|
+
data = await res.json()
|
|
208
|
+
} catch (_) {
|
|
209
|
+
data = await res.text()
|
|
210
|
+
}
|
|
211
|
+
else data = await res.text()
|
|
212
|
+
|
|
213
|
+
return new EdenFetchError(res.status, data)
|
|
214
|
+
}
|
|
174
215
|
|
|
175
216
|
if (
|
|
176
217
|
res.headers
|
|
@@ -195,12 +236,18 @@ const createProxy = (
|
|
|
195
236
|
}
|
|
196
237
|
}) as unknown as Record<string, unknown>
|
|
197
238
|
|
|
198
|
-
export const eden = <App extends Elysia<any>>(
|
|
239
|
+
export const eden = <App extends Elysia<any>>(
|
|
240
|
+
domain: string,
|
|
241
|
+
config: EdenConfig = {}
|
|
242
|
+
): Eden<App> =>
|
|
199
243
|
new Proxy(
|
|
200
244
|
{},
|
|
201
245
|
{
|
|
202
|
-
get(target, key
|
|
203
|
-
|
|
246
|
+
get(target, key) {
|
|
247
|
+
if (key === '$fn')
|
|
248
|
+
return createFn(domain, [], new Signal(domain, config))
|
|
249
|
+
|
|
250
|
+
return createProxy(domain, key as string, config)
|
|
204
251
|
}
|
|
205
252
|
}
|
|
206
253
|
) as any
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Elysia,
|
|
3
|
+
SCHEMA,
|
|
4
|
+
TypedRoute,
|
|
5
|
+
IsPathParameter,
|
|
6
|
+
EXPOSED
|
|
7
|
+
} from 'elysia'
|
|
2
8
|
import type { TObject } from '@sinclair/typebox'
|
|
3
9
|
|
|
4
10
|
import type { EdenWS } from '.'
|
|
11
|
+
import { type EdenFetchError, type Signal } from './utils'
|
|
5
12
|
|
|
6
13
|
type IsAny<T> = unknown extends T
|
|
7
14
|
? [T] extends [object]
|
|
@@ -9,16 +16,44 @@ type IsAny<T> = unknown extends T
|
|
|
9
16
|
: false
|
|
10
17
|
: false
|
|
11
18
|
|
|
12
|
-
|
|
19
|
+
type Promisify<T extends (...args: any[]) => any> = T extends (
|
|
20
|
+
...args: infer Args
|
|
21
|
+
) => infer Return
|
|
22
|
+
? Return extends Promise<any>
|
|
23
|
+
? T
|
|
24
|
+
: (...args: Args) => Promise<Return>
|
|
25
|
+
: never
|
|
26
|
+
|
|
27
|
+
type Asynctify<T> = T extends infer Fn extends (...args: any) => any
|
|
28
|
+
? Promisify<Fn>
|
|
29
|
+
: T extends Record<string, any>
|
|
30
|
+
? {
|
|
31
|
+
[K in keyof T]: EdenFn<T[K]>
|
|
32
|
+
}
|
|
33
|
+
: never
|
|
34
|
+
|
|
35
|
+
type EdenFn<T> = T extends {
|
|
36
|
+
[EXPOSED]: any
|
|
37
|
+
value: infer Value
|
|
38
|
+
}
|
|
39
|
+
? Asynctify<Value>
|
|
40
|
+
: Asynctify<T>
|
|
41
|
+
|
|
42
|
+
type CreateEdenFn<Exposed extends Record<string, any>> = EdenFn<Exposed> & {
|
|
43
|
+
$set(config: EdenConfig): void
|
|
44
|
+
$clone(config?: EdenConfig): CreateEdenFn<Exposed>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type Eden<App extends Elysia<any>> = App['meta'] extends {
|
|
13
48
|
[key in typeof SCHEMA]: infer Schema extends Record<
|
|
14
49
|
string,
|
|
15
50
|
Record<string, TypedRoute>
|
|
16
51
|
>
|
|
17
52
|
}
|
|
18
|
-
?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
:
|
|
53
|
+
? UnionToIntersection<CreateEden<Schema>> & {
|
|
54
|
+
$fn: CreateEdenFn<App['meta'][typeof EXPOSED]>
|
|
55
|
+
}
|
|
56
|
+
: 'Please install Elysia before using Eden'
|
|
22
57
|
|
|
23
58
|
export interface EdenCall {
|
|
24
59
|
[x: string]: any
|
|
@@ -32,6 +67,11 @@ export type UnionToIntersection<U> = (
|
|
|
32
67
|
? I
|
|
33
68
|
: never
|
|
34
69
|
|
|
70
|
+
// https://twitter.com/mattpocockuk/status/1622730173446557697?s=20
|
|
71
|
+
type Prettify<T> = {
|
|
72
|
+
[K in keyof T]: T[K]
|
|
73
|
+
} & {}
|
|
74
|
+
|
|
35
75
|
type TypedRouteToParams<Route extends TypedRoute> =
|
|
36
76
|
(Route['body'] extends NonNullable<Route['body']>
|
|
37
77
|
? Route['body'] extends Record<any, any>
|
|
@@ -78,75 +118,58 @@ export type CreateEden<
|
|
|
78
118
|
// Check if is method
|
|
79
119
|
[key in keyof Server[Full] extends string
|
|
80
120
|
? Lowercase<keyof Server[Full]>
|
|
81
|
-
: keyof Server[Full]]:
|
|
121
|
+
: keyof Server[Full]]: [
|
|
82
122
|
Server[Full][key extends string ? Uppercase<key> : key]
|
|
83
|
-
|
|
84
|
-
?
|
|
85
|
-
?
|
|
86
|
-
Server[Full][key]['query']
|
|
87
|
-
>
|
|
88
|
-
? (params?: {
|
|
89
|
-
$query?: EdenCall['$query']
|
|
90
|
-
}) => EdenWS<Server[Full][key]>
|
|
91
|
-
: Server[Full][key]['query'] extends NonNullable<
|
|
92
|
-
Server[Full][key]['query']
|
|
93
|
-
>
|
|
94
|
-
? (params: {
|
|
95
|
-
$query: Server[Full][key]['query']
|
|
96
|
-
}) => EdenWS<Server[Full][key]>
|
|
97
|
-
: (params?: {
|
|
98
|
-
$query?: EdenCall['$query']
|
|
99
|
-
}) => EdenWS<Server[Full][key]>
|
|
100
|
-
: (params?: {
|
|
123
|
+
] extends [infer Route extends TypedRoute]
|
|
124
|
+
? undefined extends Route['body']
|
|
125
|
+
? (params?: {
|
|
101
126
|
$query?: EdenCall['$query']
|
|
102
127
|
$fetch?: EdenCall['$fetch']
|
|
103
128
|
}) => Promise<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
Route['response'] extends {
|
|
130
|
+
200: infer ReturnedType
|
|
131
|
+
}
|
|
132
|
+
?
|
|
133
|
+
| ReturnedType
|
|
134
|
+
| MapError<Route['response']>
|
|
135
|
+
: unknown
|
|
136
|
+
>
|
|
137
|
+
: (
|
|
138
|
+
params: Prettify<
|
|
139
|
+
TypedRouteToParams<Route> & {
|
|
140
|
+
$query?: EdenCall['$query']
|
|
141
|
+
$fetch?: EdenCall['$fetch']
|
|
142
|
+
}
|
|
143
|
+
>
|
|
144
|
+
) => Promise<
|
|
145
|
+
Route['response'] extends {
|
|
146
|
+
200: infer ReturnedType
|
|
147
|
+
}
|
|
148
|
+
?
|
|
149
|
+
| ReturnedType
|
|
150
|
+
| MapError<Route['response']>
|
|
114
151
|
: unknown
|
|
115
152
|
>
|
|
116
153
|
: key extends 'subscribe'
|
|
117
|
-
?
|
|
154
|
+
? // Since subscribe key is only a lower letter
|
|
155
|
+
[
|
|
156
|
+
Server[Full][key],
|
|
118
157
|
Server[Full][key]['query']
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Server[Full][key extends string
|
|
135
|
-
? Uppercase<key>
|
|
136
|
-
: key]
|
|
137
|
-
> & {
|
|
138
|
-
$query?: EdenCall['$query']
|
|
139
|
-
$fetch?: EdenCall['$fetch']
|
|
140
|
-
}
|
|
141
|
-
) => Promise<
|
|
142
|
-
Server[Full][key extends string
|
|
143
|
-
? Uppercase<key>
|
|
144
|
-
: key]['response'] extends {
|
|
145
|
-
200: infer ReturnedType
|
|
146
|
-
}
|
|
147
|
-
? ReturnedType
|
|
148
|
-
: unknown
|
|
149
|
-
>
|
|
158
|
+
] extends [
|
|
159
|
+
infer Route extends TypedRoute,
|
|
160
|
+
infer Query extends TypedRoute['query']
|
|
161
|
+
]
|
|
162
|
+
? unknown extends NonNullable<Query>
|
|
163
|
+
? (params?: {
|
|
164
|
+
$query?: EdenCall['$query']
|
|
165
|
+
}) => EdenWS<Route>
|
|
166
|
+
: Query extends NonNullable<Query>
|
|
167
|
+
? (params: { $query: Query }) => EdenWS<Route>
|
|
168
|
+
: (params?: {
|
|
169
|
+
$query?: EdenCall['$query']
|
|
170
|
+
}) => EdenWS<Route>
|
|
171
|
+
: never
|
|
172
|
+
: never
|
|
150
173
|
}
|
|
151
174
|
: never
|
|
152
175
|
} & (Path extends `:${infer params}`
|
|
@@ -171,3 +194,33 @@ export type EdenWSEvent<
|
|
|
171
194
|
K extends keyof WebSocketEventMap,
|
|
172
195
|
Data = unknown
|
|
173
196
|
> = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K]
|
|
197
|
+
|
|
198
|
+
export interface EdenConfig {
|
|
199
|
+
fn?: string
|
|
200
|
+
fetch?: Omit<RequestInit, 'body'>
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type Enumerate<
|
|
204
|
+
N extends number,
|
|
205
|
+
Acc extends number[] = []
|
|
206
|
+
> = Acc['length'] extends N
|
|
207
|
+
? Acc[number]
|
|
208
|
+
: Enumerate<N, [...Acc, Acc['length']]>
|
|
209
|
+
|
|
210
|
+
// https://stackoverflow.com/a/39495173
|
|
211
|
+
type Range<F extends number, T extends number> = Exclude<
|
|
212
|
+
Enumerate<T>,
|
|
213
|
+
Enumerate<F>
|
|
214
|
+
>
|
|
215
|
+
|
|
216
|
+
type ErrorRange = Range<300, 599>
|
|
217
|
+
|
|
218
|
+
type MapError<T extends Record<number, unknown>> = [
|
|
219
|
+
{
|
|
220
|
+
[K in keyof T]-?: K extends ErrorRange ? K : never
|
|
221
|
+
}[keyof T]
|
|
222
|
+
] extends [infer A extends number]
|
|
223
|
+
? {
|
|
224
|
+
[K in A]: EdenFetchError<K, T[K]>
|
|
225
|
+
}[A]
|
|
226
|
+
: false
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { serialize, deserialize } from 'superjson'
|
|
2
|
+
import type { EdenCall, EdenConfig } from './types'
|
|
3
|
+
|
|
4
|
+
export class EdenFetchError<
|
|
5
|
+
Status extends number = number,
|
|
6
|
+
Value = unknown
|
|
7
|
+
> extends Error {
|
|
8
|
+
status: Status
|
|
9
|
+
value: Value
|
|
10
|
+
|
|
11
|
+
constructor(status: Status, value: Value) {
|
|
12
|
+
super()
|
|
13
|
+
|
|
14
|
+
this.status = status
|
|
15
|
+
this.value = value
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const camelToDash = (str: string) =>
|
|
21
|
+
str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
22
|
+
|
|
23
|
+
export const composePath = (
|
|
24
|
+
domain: string,
|
|
25
|
+
path: string,
|
|
26
|
+
query: EdenCall['$query'] | undefined
|
|
27
|
+
) => {
|
|
28
|
+
if (!domain.endsWith('/')) domain += '/'
|
|
29
|
+
path = camelToDash(path.replace(/index/g, ''))
|
|
30
|
+
|
|
31
|
+
if (!query || !Object.keys(query).length) return `${domain}${path}`
|
|
32
|
+
|
|
33
|
+
let q = ''
|
|
34
|
+
for (const [key, value] of Object.entries(query)) q += `${key}=${value}&`
|
|
35
|
+
|
|
36
|
+
return `${domain}${path}?${q.slice(0, -1)}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class Signal {
|
|
40
|
+
private url: string
|
|
41
|
+
private config: EdenConfig
|
|
42
|
+
|
|
43
|
+
private pendings: Array<{ n: string[] } | { n: string[]; p: any }> = []
|
|
44
|
+
private operation: Promise<any[]> | null = null
|
|
45
|
+
private isFetching = false
|
|
46
|
+
|
|
47
|
+
private sJson: Promise<{
|
|
48
|
+
serialize: typeof serialize
|
|
49
|
+
deserialize: typeof deserialize
|
|
50
|
+
}>
|
|
51
|
+
|
|
52
|
+
constructor(url: string, config: EdenConfig) {
|
|
53
|
+
this.url = url
|
|
54
|
+
this.config = config
|
|
55
|
+
|
|
56
|
+
this.sJson = import('superjson').then((superJson) => {
|
|
57
|
+
return {
|
|
58
|
+
serialize: superJson.serialize,
|
|
59
|
+
deserialize: superJson.deserialize
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setConfig(config: EdenConfig) {
|
|
65
|
+
this.config = config
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
clone(config?: EdenConfig) {
|
|
69
|
+
return new Signal(this.url, config ?? this.config)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async run(procedure: string[], params: any) {
|
|
73
|
+
const current = +this.pendings.length
|
|
74
|
+
this.pendings.push(
|
|
75
|
+
params !== undefined
|
|
76
|
+
? { n: procedure, p: params }
|
|
77
|
+
: { n: procedure }
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (this.isFetching) return this.operation?.then((x) => x[current])
|
|
81
|
+
this.isFetching = true
|
|
82
|
+
|
|
83
|
+
this.operation = new Promise((resolve) => {
|
|
84
|
+
setTimeout(async () => {
|
|
85
|
+
const requests = [...this.pendings]
|
|
86
|
+
this.pendings = []
|
|
87
|
+
|
|
88
|
+
const { serialize, deserialize } = await this.sJson
|
|
89
|
+
|
|
90
|
+
const results = await fetch(
|
|
91
|
+
`${this.url}${this.config.fn ?? '/~fn'}`,
|
|
92
|
+
{
|
|
93
|
+
method: 'POST',
|
|
94
|
+
...this.config.fetch,
|
|
95
|
+
headers: {
|
|
96
|
+
'content-type': 'elysia/fn',
|
|
97
|
+
...this.config.fetch?.headers
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify(serialize(requests))
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if (results.status === 200)
|
|
104
|
+
resolve(results.json().then((x) => deserialize(x as any)))
|
|
105
|
+
else
|
|
106
|
+
resolve(
|
|
107
|
+
Array(requests.length).fill(
|
|
108
|
+
new Error(await results.text())
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
}, 33)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const result = await this.operation.then((results) => results[current])
|
|
115
|
+
|
|
116
|
+
this.operation = null
|
|
117
|
+
this.isFetching = false
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
}
|
|
121
|
+
}
|