@elysiajs/eden 0.1.0 → 0.2.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/README.md CHANGED
@@ -33,13 +33,13 @@ import type { App } from './server'
33
33
  const client = eden<App>('http://localhost:8080')
34
34
 
35
35
  // return: Hi Elysia (fully type-safe)
36
- client.index.GET().then(console.log)
36
+ client.index.get().then(console.log)
37
37
 
38
38
  // return: 1895
39
- client.id.1895.GET().then(console.log)
39
+ client.id.1895.get().then(console.log)
40
40
 
41
41
  // return: { id: 1895, name: 'Skadi' }
42
- client.mirror.POST({
42
+ client.mirror.post({
43
43
  id: 1895,
44
44
  name: 'Skadi'
45
45
  }).then(console.log)
package/dist/index.d.ts CHANGED
@@ -1,3 +1,15 @@
1
- import type { Elysia } from 'elysia';
2
- import { Eden } from './types';
1
+ /// <reference types="bun-types" />
2
+ import type { Elysia, TypedSchema } from 'elysia';
3
+ import type { Eden, EdenWSEvent } from './types';
4
+ export declare class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
5
+ ws: WebSocket;
6
+ url: string;
7
+ constructor(url: string);
8
+ send(data: Schema['body'] | Schema['body'][]): this;
9
+ on<K extends keyof WebSocketEventMap>(type: K, listener: (event: EdenWSEvent<K, Schema['response']>) => void, options?: boolean | AddEventListenerOptions): this;
10
+ off<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): this;
11
+ addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (event: EdenWSEvent<K, Schema['response']>) => void, options?: boolean | AddEventListenerOptions): this;
12
+ removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): this;
13
+ close(): this;
14
+ }
3
15
  export declare const eden: <App extends Elysia<any>>(domain: string) => Eden<App>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=t=>t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),y=(t,e,r)=>{if(t.endsWith("/")||(t+="/"),e=g(e.replace(/index/g,"")),!r||!Object.keys(r).length)return`${t}${e}`;let n="";for(const[a,o]of Object.entries(r))n+=`${a}=${o}&`;return`${t}${e}?${n.slice(0,-1)}`},u=(t,e="")=>new Proxy(()=>{},{get(r,n,a){return u(t,`${e}/${n.toString()}`)},apply(r,n,[{$query:a,$fetch:o,...c}={$fetch:void 0,$query:void 0}]=[{}]){const l=e.lastIndexOf("/");return fetch(y(t,e.slice(0,l),a),{method:e.slice(l+1),headers:{"content-type":"application/json",...o==null?void 0:o.headers},body:Object.keys(c).length?JSON.stringify(c):void 0,...o}).then(async s=>{if(s.status>=300)throw new Error(await s.text());if(s.headers.get("content-type")==="application/json")return s.json();const i=await s.text();return Number.isNaN(+i)?i==="true"?!0:i==="false"?!1:i:+i})}}),f=t=>new Proxy({},{get(e,r,n){return u(t,r)}});exports.eden=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class g{constructor(e){this.ws=new WebSocket(e),this.url=e}send(e){return Array.isArray(e)?(e.forEach(t=>this.send(t)),this):(this.ws.send(typeof e=="object"?JSON.stringify(e):e.toString()),this)}on(e,t,s){return this.addEventListener(e,t,s)}off(e,t,s){return this.ws.removeEventListener(e,t,s),this}addEventListener(e,t,s){return this.ws.addEventListener(e,i=>{if(e==="message"){let r=i.data.toString();const l=r.charCodeAt(0);if(l===47||l===123)try{r=JSON.parse(r)}catch{}else Number.isNaN(+r)?r==="true"?r=!0:r==="fase"&&(r=!1):r=+r;t({...i,data:r})}else t(i)},s),this}removeEventListener(e,t,s){return this.off(e,t,s),this}close(){return this.ws.close(),this}}const y=n=>n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),w=(n,e,t)=>{if(n.endsWith("/")||(n+="/"),e=y(e.replace(/index/g,"")),!t||!Object.keys(t).length)return`${n}${e}`;let s="";for(const[i,r]of Object.entries(t))s+=`${i}=${r}&`;return`${n}${e}?${s.slice(0,-1)}`},v=(n,e="")=>new Proxy(()=>{},{get(t,s,i){return v(n,`${e}/${s.toString()}`)},apply(t,s,[{$query:i,$fetch:r,$body:l,...u}={$fetch:void 0,$query:void 0,$body:void 0}]=[{}]){const f=e.lastIndexOf("/"),h=e.slice(f+1),d=w(n,e.slice(0,f),i);if(h==="subscribe")return new g(d.replace(/^([^]+):\/\//,"ws://"));const o=l??(Object.keys(u).length?u:void 0);return fetch(d,{method:h,body:JSON.stringify(o),headers:o?{"content-type":typeof u=="object"?"application/json":"text/plain","content-length":o==null?void 0:o.length,...r==null?void 0:r.headers}:void 0,...r}).then(async c=>{if(c.status>=300)throw new Error(await c.text());if(c.headers.get("content-type")==="application/json")return c.json();const a=await c.text();return Number.isNaN(+a)?a==="true"?!0:a==="false"?!1:a:+a})}}),S=n=>new Proxy({},{get(e,t,s){return v(n,t)}});exports.EdenWS=g;exports.eden=S;
package/dist/index.mjs CHANGED
@@ -1,47 +1,99 @@
1
- const g = (t) => t.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`), f = (t, e, r) => {
2
- if (t.endsWith("/") || (t += "/"), e = g(e.replace(/index/g, "")), !r || !Object.keys(r).length)
3
- return `${t}${e}`;
4
- let n = "";
5
- for (const [a, o] of Object.entries(r))
6
- n += `${a}=${o}&`;
7
- return `${t}${e}?${n.slice(0, -1)}`;
8
- }, u = (t, e = "") => new Proxy(() => {
1
+ class v {
2
+ constructor(e) {
3
+ this.ws = new WebSocket(e), this.url = e;
4
+ }
5
+ send(e) {
6
+ return Array.isArray(e) ? (e.forEach((t) => this.send(t)), this) : (this.ws.send(
7
+ typeof e == "object" ? JSON.stringify(e) : e.toString()
8
+ ), this);
9
+ }
10
+ on(e, t, s) {
11
+ return this.addEventListener(e, t, s);
12
+ }
13
+ off(e, t, s) {
14
+ return this.ws.removeEventListener(e, t, s), this;
15
+ }
16
+ addEventListener(e, t, s) {
17
+ return this.ws.addEventListener(
18
+ e,
19
+ (i) => {
20
+ if (e === "message") {
21
+ let r = i.data.toString();
22
+ const l = r.charCodeAt(0);
23
+ if (l === 47 || l === 123)
24
+ try {
25
+ r = JSON.parse(r);
26
+ } catch {
27
+ }
28
+ else
29
+ Number.isNaN(+r) ? r === "true" ? r = !0 : r === "fase" && (r = !1) : r = +r;
30
+ t({
31
+ ...i,
32
+ data: r
33
+ });
34
+ } else
35
+ t(i);
36
+ },
37
+ s
38
+ ), this;
39
+ }
40
+ removeEventListener(e, t, s) {
41
+ return this.off(e, t, s), this;
42
+ }
43
+ close() {
44
+ return this.ws.close(), this;
45
+ }
46
+ }
47
+ const y = (n) => n.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`), w = (n, e, t) => {
48
+ if (n.endsWith("/") || (n += "/"), e = y(e.replace(/index/g, "")), !t || !Object.keys(t).length)
49
+ return `${n}${e}`;
50
+ let s = "";
51
+ for (const [i, r] of Object.entries(t))
52
+ s += `${i}=${r}&`;
53
+ return `${n}${e}?${s.slice(0, -1)}`;
54
+ }, d = (n, e = "") => new Proxy(() => {
9
55
  }, {
10
- get(r, n, a) {
11
- return u(t, `${e}/${n.toString()}`);
56
+ get(t, s, i) {
57
+ return d(n, `${e}/${s.toString()}`);
12
58
  },
13
- apply(r, n, [
14
- { $query: a, $fetch: o, ...c } = {
59
+ apply(t, s, [
60
+ { $query: i, $fetch: r, $body: l, ...u } = {
15
61
  $fetch: void 0,
16
- $query: void 0
62
+ $query: void 0,
63
+ $body: void 0
17
64
  }
18
65
  ] = [{}]) {
19
- const l = e.lastIndexOf("/");
20
- return fetch(f(t, e.slice(0, l), a), {
21
- method: e.slice(l + 1),
22
- headers: {
23
- "content-type": "application/json",
24
- ...o == null ? void 0 : o.headers
25
- },
26
- body: Object.keys(c).length ? JSON.stringify(c) : void 0,
27
- ...o
28
- }).then(async (s) => {
29
- if (s.status >= 300)
30
- throw new Error(await s.text());
31
- if (s.headers.get("content-type") === "application/json")
32
- return s.json();
33
- const i = await s.text();
34
- return Number.isNaN(+i) ? i === "true" ? !0 : i === "false" ? !1 : i : +i;
66
+ const f = e.lastIndexOf("/"), h = e.slice(f + 1), g = w(n, e.slice(0, f), i);
67
+ if (h === "subscribe")
68
+ return new v(g.replace(/^([^]+):\/\//, "ws://"));
69
+ const o = l ?? (Object.keys(u).length ? u : void 0);
70
+ return fetch(g, {
71
+ method: h,
72
+ body: JSON.stringify(o),
73
+ headers: o ? {
74
+ "content-type": typeof u == "object" ? "application/json" : "text/plain",
75
+ "content-length": o == null ? void 0 : o.length,
76
+ ...r == null ? void 0 : r.headers
77
+ } : void 0,
78
+ ...r
79
+ }).then(async (c) => {
80
+ if (c.status >= 300)
81
+ throw new Error(await c.text());
82
+ if (c.headers.get("content-type") === "application/json")
83
+ return c.json();
84
+ const a = await c.text();
85
+ return Number.isNaN(+a) ? a === "true" ? !0 : a === "false" ? !1 : a : +a;
35
86
  });
36
87
  }
37
- }), y = (t) => new Proxy(
88
+ }), p = (n) => new Proxy(
38
89
  {},
39
90
  {
40
- get(e, r, n) {
41
- return u(t, r);
91
+ get(e, t, s) {
92
+ return d(n, t);
42
93
  }
43
94
  }
44
95
  );
45
96
  export {
46
- y as eden
97
+ v as EdenWS,
98
+ p as eden
47
99
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(r,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(r=typeof globalThis<"u"?globalThis:r||self,s(r["@elysia/eden"]={}))})(this,function(r){"use strict";const s=t=>t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),y=(t,e,n)=>{if(t.endsWith("/")||(t+="/"),e=s(e.replace(/index/g,"")),!n||!Object.keys(n).length)return`${t}${e}`;let o="";for(const[c,i]of Object.entries(n))o+=`${c}=${i}&`;return`${t}${e}?${o.slice(0,-1)}`},a=(t,e="")=>new Proxy(()=>{},{get(n,o,c){return a(t,`${e}/${o.toString()}`)},apply(n,o,[{$query:c,$fetch:i,...f}={$fetch:void 0,$query:void 0}]=[{}]){const d=e.lastIndexOf("/");return fetch(y(t,e.slice(0,d),c),{method:e.slice(d+1),headers:{"content-type":"application/json",...i==null?void 0:i.headers},body:Object.keys(f).length?JSON.stringify(f):void 0,...i}).then(async u=>{if(u.status>=300)throw new Error(await u.text());if(u.headers.get("content-type")==="application/json")return u.json();const l=await u.text();return Number.isNaN(+l)?l==="true"?!0:l==="false"?!1:l:+l})}}),g=t=>new Proxy({},{get(e,n,o){return a(t,n)}});r.eden=g,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
1
+ (function(o,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(o=typeof globalThis<"u"?globalThis:o||self,c(o["@elysia/eden"]={}))})(this,function(o){"use strict";class c{constructor(e){this.ws=new WebSocket(e),this.url=e}send(e){return Array.isArray(e)?(e.forEach(t=>this.send(t)),this):(this.ws.send(typeof e=="object"?JSON.stringify(e):e.toString()),this)}on(e,t,r){return this.addEventListener(e,t,r)}off(e,t,r){return this.ws.removeEventListener(e,t,r),this}addEventListener(e,t,r){return this.ws.addEventListener(e,i=>{if(e==="message"){let n=i.data.toString();const a=n.charCodeAt(0);if(a===47||a===123)try{n=JSON.parse(n)}catch{}else Number.isNaN(+n)?n==="true"?n=!0:n==="fase"&&(n=!1):n=+n;t({...i,data:n})}else t(i)},r),this}removeEventListener(e,t,r){return this.off(e,t,r),this}close(){return this.ws.close(),this}}const p=s=>s.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),w=(s,e,t)=>{if(s.endsWith("/")||(s+="/"),e=p(e.replace(/index/g,"")),!t||!Object.keys(t).length)return`${s}${e}`;let r="";for(const[i,n]of Object.entries(t))r+=`${i}=${n}&`;return`${s}${e}?${r.slice(0,-1)}`},h=(s,e="")=>new Proxy(()=>{},{get(t,r,i){return h(s,`${e}/${r.toString()}`)},apply(t,r,[{$query:i,$fetch:n,$body:a,...d}={$fetch:void 0,$query:void 0,$body:void 0}]=[{}]){const g=e.lastIndexOf("/"),y=e.slice(g+1),v=w(s,e.slice(0,g),i);if(y==="subscribe")return new c(v.replace(/^([^]+):\/\//,"ws://"));const u=a??(Object.keys(d).length?d:void 0);return fetch(v,{method:y,body:JSON.stringify(u),headers:u?{"content-type":typeof d=="object"?"application/json":"text/plain","content-length":u==null?void 0:u.length,...n==null?void 0:n.headers}:void 0,...n}).then(async f=>{if(f.status>=300)throw new Error(await f.text());if(f.headers.get("content-type")==="application/json")return f.json();const l=await f.text();return Number.isNaN(+l)?l==="true"?!0:l==="false"?!1:l:+l})}}),S=s=>new Proxy({},{get(e,t,r){return h(s,t)}});o.EdenWS=c,o.eden=S,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
package/dist/types.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /// <reference types="bun-types" />
2
2
  import type { Elysia, SCHEMA, TypedRoute } from 'elysia';
3
+ import type { IsPathParameter } from 'elysia/dist/types';
4
+ import type { EdenWS } from '.';
3
5
  export declare type Eden<App extends Elysia<any>> = App['store'] extends {
4
6
  [key in typeof SCHEMA]: any;
5
7
  } ? UnionToIntersection<CreateEden<App['store'][typeof SCHEMA]>> : never;
@@ -9,21 +11,42 @@ export interface EdenCall {
9
11
  $query?: Record<string, string | boolean | number>;
10
12
  }
11
13
  export declare type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
12
- declare type TypedRouteToParams<Route extends TypedRoute> = (Route['body'] extends NonNullable<Route['body']> ? Route['body'] : {}) & (Route['query'] extends NonNullable<Route['query']> ? {
14
+ declare type TypedRouteToParams<Route extends TypedRoute> = (Route['body'] extends NonNullable<Route['body']> ? Route['body'] extends Record<any, any> ? Route['body'] : {
15
+ $body: Route['body'];
16
+ } : {}) & (Route['query'] extends NonNullable<Route['query']> ? unknown extends Route['query'] ? {} : {
13
17
  $query: Route['query'];
14
18
  } : {});
15
- export declare type CreateEden<Server extends Record<string, Record<string, TypedRoute>>, Path extends string = keyof Server, Full extends string = ''> = Path extends `/${infer Start}` ? CreateEden<Server, Start, Path> : Path extends `${infer A}/${infer B}` ? {
19
+ export declare type CreateEden<Server extends Record<string, Record<string, TypedRoute>>, Path extends string = keyof Server, Full extends string = ''> = Path extends `/${infer Start}` ? CreateEden<Server, Start, Path> : Path extends `${infer A}/${infer B}` ? IsPathParameter<A> extends never ? {
16
20
  [key in A]: CreateEden<Server, B, Full>;
21
+ } : {
22
+ [x: string]: CreateEden<Server, B, Full>;
17
23
  } : {
18
24
  [key in Path extends '' ? 'index' : Path extends `:${infer params}` ? string : Path | CamelCase<Path>]: Full extends keyof Server ? {
19
- [key in keyof Server[Full]]: keyof TypedRouteToParams<Server[Full][key]> extends never ? (params?: {
25
+ [key in keyof Server[Full] extends string ? Lowercase<keyof Server[Full]> : keyof Server[Full]]: keyof TypedRouteToParams<Server[Full][key extends string ? Uppercase<key> : key]> extends never ? key extends 'subscribe' ? unknown extends NonNullable<Server[Full][key]['query']> ? (params?: {
26
+ $query?: EdenCall['$query'];
27
+ }) => EdenWS<Server[Full][key]> : Server[Full][key]['query'] extends NonNullable<Server[Full][key]['query']> ? (params: {
28
+ $query: Server[Full][key]['query'];
29
+ }) => EdenWS<Server[Full][key]> : (params?: {
30
+ $query?: EdenCall['$query'];
31
+ }) => EdenWS<Server[Full][key]> : (params?: {
20
32
  $query?: EdenCall['$query'];
21
33
  $fetch?: EdenCall['$fetch'];
22
- }) => Promise<Server[Full][key]['response']> : (params: TypedRouteToParams<Server[Full][key]> & {
34
+ }) => Promise<Server[Full][key]['response']> : key extends 'subscribe' ? unknown extends NonNullable<Server[Full][key]['query']> ? (params?: {
35
+ $query?: EdenCall['$query'];
36
+ }) => EdenWS<Server[Full][key]> : Server[Full][key]['query'] extends NonNullable<Server[Full][key]['query']> ? (params: {
37
+ $query: Server[Full][key]['query'];
38
+ }) => EdenWS<Server[Full][key]> : (params?: {
39
+ $query?: EdenCall['$query'];
40
+ }) => EdenWS<Server[Full][key]> : (params: TypedRouteToParams<Server[Full][key extends string ? Uppercase<key> : key]> & {
23
41
  $query?: EdenCall['$query'];
24
42
  $fetch?: EdenCall['$fetch'];
25
- }) => Promise<Server[Full][key]['response']>;
43
+ }) => Promise<Server[Full][key extends string ? Uppercase<key> : key]['response']>;
26
44
  } : never;
27
45
  };
28
46
  declare type CamelCase<S extends string> = S extends `${infer P1}-${infer P2}${infer P3}` ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}` : Lowercase<S>;
47
+ export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
48
+ data: Data;
49
+ rawData: MessageEvent['data'];
50
+ }
51
+ export declare type EdenWSEvent<K extends keyof WebSocketEventMap, Data = unknown> = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K];
29
52
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elysiajs/eden",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-rc.1",
4
4
  "description": "Fully type-safe Elysia client",
5
5
  "author": {
6
6
  "name": "saltyAom",
@@ -33,15 +33,15 @@
33
33
  "build": "vite build",
34
34
  "release": "npm run build && npm run test && npm publish --access public"
35
35
  },
36
- "peerDependencies": {
37
- "elysia": ">= 0.1.0-rc.5"
36
+ "dependencies": {
37
+ "elysia": "^0.1.2"
38
38
  },
39
39
  "devDependencies": {
40
- "@elysiajs/cors": "^0.1.0-rc.3",
40
+ "@elysiajs/cors": "^0.1.0",
41
+ "@elysiajs/websocket": "^0.2.1",
41
42
  "@sinclair/typebox": "^0.25.13",
42
43
  "@types/node": "^18.11.7",
43
44
  "bun-types": "^0.3.0",
44
- "elysia": "^0.1.0-rc.8",
45
45
  "eslint": "^8.26.0",
46
46
  "rimraf": "^3.0.2",
47
47
  "typescript": "^4.8.4",
package/src/index.ts CHANGED
@@ -1,7 +1,106 @@
1
- import type { Elysia } from 'elysia'
2
- import type { HTTPMethod } from 'elysia'
1
+ import type { Elysia, TypedSchema, HTTPMethod } from 'elysia'
3
2
 
4
- import { CreateEden, Eden, EdenCall, UnionToIntersection } from './types'
3
+ import type {
4
+ CreateEden,
5
+ Eden,
6
+ EdenCall,
7
+ EdenWSEvent,
8
+ UnionToIntersection
9
+ } from './types'
10
+
11
+ export class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
12
+ ws: WebSocket
13
+ url: string
14
+
15
+ constructor(url: string) {
16
+ this.ws = new WebSocket(url)
17
+ this.url = url
18
+ }
19
+
20
+ send(data: Schema['body'] | Schema['body'][]) {
21
+ if (Array.isArray(data)) {
22
+ data.forEach((datum) => this.send(datum))
23
+
24
+ return this
25
+ }
26
+
27
+ this.ws.send(
28
+ typeof data === 'object' ? JSON.stringify(data) : data.toString()
29
+ )
30
+
31
+ return this
32
+ }
33
+
34
+ on<K extends keyof WebSocketEventMap>(
35
+ type: K,
36
+ listener: (event: EdenWSEvent<K, Schema['response']>) => void,
37
+ options?: boolean | AddEventListenerOptions
38
+ ) {
39
+ return this.addEventListener(type, listener, options)
40
+ }
41
+
42
+ off<K extends keyof WebSocketEventMap>(
43
+ type: K,
44
+ listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
45
+ options?: boolean | EventListenerOptions
46
+ ) {
47
+ this.ws.removeEventListener(type, listener, options)
48
+
49
+ return this
50
+ }
51
+
52
+ addEventListener<K extends keyof WebSocketEventMap>(
53
+ type: K,
54
+ listener: (event: EdenWSEvent<K, Schema['response']>) => void,
55
+ options?: boolean | AddEventListenerOptions
56
+ ) {
57
+ this.ws.addEventListener(
58
+ type,
59
+ (ws) => {
60
+ if (type === 'message') {
61
+ let data = (ws as MessageEvent).data.toString() as any
62
+ const start = data.charCodeAt(0)
63
+
64
+ if (start === 47 || start === 123)
65
+ try {
66
+ data = JSON.parse(data)
67
+ } catch {}
68
+ else if (!Number.isNaN(+data)) data = +data
69
+ else if (data === 'true') data = true
70
+ else if (data === 'fase') data = false
71
+
72
+ // @ts-ignore
73
+ listener({
74
+ ...ws,
75
+ data
76
+ })
77
+ } else {
78
+ // @ts-ignore
79
+ listener(ws)
80
+ }
81
+ },
82
+ options
83
+ )
84
+
85
+ return this
86
+ }
87
+
88
+ removeEventListener<K extends keyof WebSocketEventMap>(
89
+ type: K,
90
+ listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
91
+ options?: boolean | EventListenerOptions
92
+ ) {
93
+ this.off(type, listener, options)
94
+
95
+ return this
96
+ }
97
+
98
+ close() {
99
+ this.ws.close()
100
+
101
+ return this
102
+ }
103
+ }
5
104
 
6
105
  const camelToDash = (str: string) =>
7
106
  str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
@@ -34,22 +133,35 @@ const createProxy = (
34
133
  target,
35
134
  _,
36
135
  [
37
- { $query, $fetch, ...body } = {
136
+ { $query, $fetch, $body, ...bodyObj } = {
38
137
  $fetch: undefined,
39
- $query: undefined
138
+ $query: undefined,
139
+ $body: undefined
40
140
  }
41
141
  ]: EdenCall[] = [{}]
42
142
  ) {
43
- const i = path.lastIndexOf('/')
44
-
45
- return fetch(composePath(domain, path.slice(0, i), $query), {
46
- method: path.slice(i + 1),
47
- headers: {
48
- 'content-type': 'application/json',
49
- ...$fetch?.['headers']
50
- },
51
- body: Object.keys(body).length
52
- ? JSON.stringify(body)
143
+ const i = path.lastIndexOf('/'),
144
+ method = path.slice(i + 1),
145
+ url = composePath(domain, path.slice(0, i), $query)
146
+
147
+ if (method === 'subscribe')
148
+ return new EdenWS(url.replace(/^([^]+):\/\//, 'ws://'))
149
+
150
+ const body =
151
+ $body ?? (Object.keys(bodyObj).length ? bodyObj : undefined)
152
+
153
+ return fetch(url, {
154
+ method,
155
+ body: JSON.stringify(body),
156
+ headers: body
157
+ ? {
158
+ 'content-type':
159
+ typeof bodyObj === 'object'
160
+ ? 'application/json'
161
+ : 'text/plain',
162
+ 'content-length': body?.length,
163
+ ...$fetch?.['headers']
164
+ }
53
165
  : undefined,
54
166
  ...$fetch
55
167
  }).then(async (res) => {
package/src/types.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import type { Elysia, SCHEMA, TypedRoute } from 'elysia'
2
+ import type { IsPathParameter } from 'elysia/dist/types'
3
+ import { TObject } from '@sinclair/typebox'
4
+
5
+ import type { EdenWS } from '.'
2
6
 
3
7
  export type Eden<App extends Elysia<any>> = App['store'] extends {
4
8
  [key in typeof SCHEMA]: any
@@ -6,7 +10,6 @@ export type Eden<App extends Elysia<any>> = App['store'] extends {
6
10
  ? UnionToIntersection<CreateEden<App['store'][typeof SCHEMA]>>
7
11
  : never
8
12
 
9
-
10
13
  export interface EdenCall {
11
14
  [x: string]: any
12
15
  $fetch?: RequestInit
@@ -20,11 +23,19 @@ export type UnionToIntersection<U> = (
20
23
  : never
21
24
 
22
25
  type TypedRouteToParams<Route extends TypedRoute> =
23
- (Route['body'] extends NonNullable<Route['body']> ? Route['body'] : {}) &
24
- (Route['query'] extends NonNullable<Route['query']>
25
- ? {
26
- $query: Route['query']
26
+ (Route['body'] extends NonNullable<Route['body']>
27
+ ? Route['body'] extends Record<any, any>
28
+ ? Route['body']
29
+ : {
30
+ $body: Route['body']
27
31
  }
32
+ : {}) &
33
+ (Route['query'] extends NonNullable<Route['query']>
34
+ ? unknown extends Route['query']
35
+ ? {}
36
+ : {
37
+ $query: Route['query']
38
+ }
28
39
  : {})
29
40
 
30
41
  export type CreateEden<
@@ -35,9 +46,13 @@ export type CreateEden<
35
46
  > = Path extends `/${infer Start}`
36
47
  ? CreateEden<Server, Start, Path>
37
48
  : Path extends `${infer A}/${infer B}`
38
- ? {
39
- [key in A]: CreateEden<Server, B, Full>
40
- }
49
+ ? IsPathParameter<A> extends never
50
+ ? {
51
+ [key in A]: CreateEden<Server, B, Full>
52
+ }
53
+ : {
54
+ [x: string]: CreateEden<Server, B, Full>
55
+ }
41
56
  : {
42
57
  [key in Path extends ''
43
58
  ? 'index'
@@ -45,19 +60,61 @@ export type CreateEden<
45
60
  ? string
46
61
  : Path | CamelCase<Path>]: Full extends keyof Server
47
62
  ? {
48
- [key in keyof Server[Full]]: keyof TypedRouteToParams<
49
- Server[Full][key]
63
+ [key in keyof Server[Full] extends string
64
+ ? Lowercase<keyof Server[Full]>
65
+ : keyof Server[Full]]: keyof TypedRouteToParams<
66
+ Server[Full][key extends string ? Uppercase<key> : key]
50
67
  > extends never
51
- ? (params?: {
52
- $query?: EdenCall['$query']
53
- $fetch?: EdenCall['$fetch']
54
- }) => Promise<Server[Full][key]['response']>
68
+ ? key extends 'subscribe'
69
+ ? unknown extends NonNullable<
70
+ Server[Full][key]['query']
71
+ >
72
+ ? (params?: {
73
+ $query?: EdenCall['$query']
74
+ }) => EdenWS<Server[Full][key]>
75
+ : Server[Full][key]['query'] extends NonNullable<
76
+ Server[Full][key]['query']
77
+ >
78
+ ? (params: {
79
+ $query: Server[Full][key]['query']
80
+ }) => EdenWS<Server[Full][key]>
81
+ : (params?: {
82
+ $query?: EdenCall['$query']
83
+ }) => EdenWS<Server[Full][key]>
84
+ : (params?: {
85
+ $query?: EdenCall['$query']
86
+ $fetch?: EdenCall['$fetch']
87
+ }) => Promise<Server[Full][key]['response']>
88
+ : key extends 'subscribe'
89
+ ? unknown extends NonNullable<
90
+ Server[Full][key]['query']
91
+ >
92
+ ? (params?: {
93
+ $query?: EdenCall['$query']
94
+ }) => EdenWS<Server[Full][key]>
95
+ : Server[Full][key]['query'] extends NonNullable<
96
+ Server[Full][key]['query']
97
+ >
98
+ ? (params: {
99
+ $query: Server[Full][key]['query']
100
+ }) => EdenWS<Server[Full][key]>
101
+ : (params?: {
102
+ $query?: EdenCall['$query']
103
+ }) => EdenWS<Server[Full][key]>
55
104
  : (
56
- params: TypedRouteToParams<Server[Full][key]> & {
105
+ params: TypedRouteToParams<
106
+ Server[Full][key extends string
107
+ ? Uppercase<key>
108
+ : key]
109
+ > & {
57
110
  $query?: EdenCall['$query']
58
111
  $fetch?: EdenCall['$fetch']
59
112
  }
60
- ) => Promise<Server[Full][key]['response']>
113
+ ) => Promise<
114
+ Server[Full][key extends string
115
+ ? Uppercase<key>
116
+ : key]['response']
117
+ >
61
118
  }
62
119
  : never
63
120
  }
@@ -68,4 +125,12 @@ type CamelCase<S extends string> =
68
125
  ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
69
126
  : Lowercase<S>
70
127
 
71
- type A = CamelCase<'sign-in'>
128
+ export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
129
+ data: Data
130
+ rawData: MessageEvent['data']
131
+ }
132
+
133
+ export type EdenWSEvent<
134
+ K extends keyof WebSocketEventMap,
135
+ Data = unknown
136
+ > = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K]