@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/src/index.ts CHANGED
@@ -1,253 +1,34 @@
1
- import type { Elysia, TypedSchema, HTTPMethod } from 'elysia'
2
-
3
- import type {
4
- CreateEden,
5
- Eden,
6
- EdenCall,
7
- EdenConfig,
8
- EdenWSEvent,
9
- UnionToIntersection
10
- } from './types'
11
- import { composePath, EdenFetchError, Signal } from './utils'
12
-
13
- export class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
14
- ws: WebSocket
15
- url: string
16
-
17
- constructor(url: string) {
18
- this.ws = new WebSocket(url)
19
- this.url = url
20
- }
21
-
22
- send(data: Schema['body'] | Schema['body'][]) {
23
- if (Array.isArray(data)) {
24
- data.forEach((datum) => this.send(datum))
25
-
26
- return this
27
- }
28
-
29
- this.ws.send(
30
- typeof data === 'object' ? JSON.stringify(data) : data.toString()
31
- )
32
-
33
- return this
34
- }
35
-
36
- on<K extends keyof WebSocketEventMap>(
37
- type: K,
38
- listener: (event: EdenWSEvent<K, Schema['response']>) => void,
39
- options?: boolean | AddEventListenerOptions
40
- ) {
41
- return this.addEventListener(type, listener, options)
42
- }
43
-
44
- off<K extends keyof WebSocketEventMap>(
45
- type: K,
46
- listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
47
- options?: boolean | EventListenerOptions
48
- ) {
49
- this.ws.removeEventListener(type, listener, options)
50
-
51
- return this
52
- }
53
-
54
- addEventListener<K extends keyof WebSocketEventMap>(
55
- type: K,
56
- listener: (event: EdenWSEvent<K, Schema['response']>) => void,
57
- options?: boolean | AddEventListenerOptions
58
- ) {
59
- this.ws.addEventListener(
60
- type,
61
- (ws) => {
62
- if (type === 'message') {
63
- let data = (ws as MessageEvent).data.toString() as any
64
- const start = data.charCodeAt(0)
65
-
66
- if (start === 47 || start === 123)
67
- try {
68
- data = JSON.parse(data)
69
- } catch {}
70
- else if (!Number.isNaN(+data)) data = +data
71
- else if (data === 'true') data = true
72
- else if (data === 'fase') data = false
73
-
74
- // @ts-ignore
75
- listener({
76
- ...ws,
77
- data
78
- })
79
- } else {
80
- // @ts-ignore
81
- listener(ws)
82
- }
83
- },
84
- options
85
- )
86
-
87
- return this
88
- }
89
-
90
- removeEventListener<K extends keyof WebSocketEventMap>(
91
- type: K,
92
- listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
93
- options?: boolean | EventListenerOptions
94
- ) {
95
- this.off(type, listener, options)
96
-
97
- return this
98
- }
99
-
100
- close() {
101
- this.ws.close()
102
-
103
- return this
104
- }
105
- }
106
-
107
- const createFn = (
108
- domain: string,
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]
119
-
120
- if (procedure.length === 1) {
121
- if (
122
- procedure[0] in Object.prototype ||
123
- procedure[0] in Promise.prototype
124
- )
125
- return target(params)
126
-
127
- switch (procedure[0]) {
128
- case 'toJSON':
129
- return target(params)
130
-
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
- })
146
-
147
- const createProxy = (
148
- domain: string,
149
- path: string = '',
150
- config: EdenConfig
151
- ): Record<string, unknown> =>
152
- new Proxy(() => {}, {
153
- get(target, key, value) {
154
- return createProxy(domain, `${path}/${key.toString()}`, config)
155
- },
156
- apply(
157
- target,
158
- _,
159
- [
160
- { $query, $fetch, $body, ...bodyObj } = {
161
- $fetch: undefined,
162
- $query: undefined,
163
- $body: undefined
164
- }
165
- ]: EdenCall[] = [{}]
166
- ) {
167
- const i = path.lastIndexOf('/'),
168
- method = path.slice(i + 1),
169
- url = composePath(domain, path.slice(0, i), $query)
170
-
171
- if (method === 'subscribe')
172
- return new EdenWS(
173
- url.replace(
174
- /^([^]+):\/\//,
175
- url.startsWith('https://') ? 'wss://' : 'ws://'
176
- )
177
- )
178
-
179
- const body =
180
- $body ?? (Object.keys(bodyObj).length ? bodyObj : undefined)
181
- const isObject = typeof body === 'object'
182
-
183
- return fetch(url, {
184
- method,
185
- body: isObject ? JSON.stringify(body) : body,
186
- ...config.fetch,
187
- ...$fetch,
188
- headers: body
189
- ? {
190
- 'content-type': isObject
191
- ? 'application/json'
192
- : 'text/plain',
193
- ...config.fetch?.headers,
194
- ...$fetch?.['headers']
195
- }
196
- : undefined
197
- }).then(async (res) => {
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
- }
215
-
216
- if (
217
- res.headers
218
- .get('content-type')
219
- ?.includes('application/json')
220
- )
221
- try {
222
- return await res.json()
223
- } catch (_) {
224
- // if json is error then it's string
225
- // flow down
226
- }
227
-
228
- let data = await res.text()
229
-
230
- if (!Number.isNaN(+data)) return +data
231
- if (data === 'true') return true
232
- if (data === 'false') return false
233
-
234
- return data
235
- })
236
- }
237
- }) as unknown as Record<string, unknown>
238
-
239
- export const eden = <App extends Elysia<any>>(
240
- domain: string,
241
- config: EdenConfig = {}
242
- ): Eden<App> =>
243
- new Proxy(
244
- {},
245
- {
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)
251
- }
252
- }
253
- ) as any
1
+ import type { Elysia } from 'elysia'
2
+
3
+ import type { EdenTreaty } from './treaty'
4
+ import type { EdenFetch } from './fetch'
5
+ import type { EdenFn } from './fn'
6
+
7
+ export { edenTreaty } from './treaty'
8
+ export { edenFetch } from './fetch'
9
+ export { edenFn } from './fn'
10
+
11
+ // @ts-ignore
12
+ // export const eden: Eden = (domain: string) => ({
13
+ // treaty(config) {
14
+ // return import('./treaty').then((x) => x.edenTreaty(domain, config))
15
+ // },
16
+ // fn(config) {
17
+ // return import('./fn').then((x) => x.edenFn(domain, config))
18
+ // },
19
+ // fetch(config) {
20
+ // return import('./fetch').then((x) => x.edenFetch(domain, config))
21
+ // }
22
+ // })
23
+
24
+ // type Eden = (domain: string) => {
25
+ // treaty<App extends Elysia<any>>(
26
+ // config?: EdenTreaty.Config
27
+ // ): EdenTreaty.Create<App>
28
+
29
+ // fetch<App extends Elysia<any>>(
30
+ // config?: EdenFetch.Config
31
+ // ): EdenFetch.Create<App>
32
+
33
+ // fn<App extends Elysia<any>>(config?: EdenFn.Config): EdenFn.Create<App>
34
+ // }
@@ -0,0 +1,203 @@
1
+ import type { Elysia, TypedSchema } from 'elysia'
2
+
3
+ import { EdenFetchError } from '../utils'
4
+
5
+ import { composePath } from './utils'
6
+ import type { EdenTreaty } from './types'
7
+
8
+ export type { EdenTreaty } from './types'
9
+
10
+ export class EdenWS<Schema extends TypedSchema<any> = TypedSchema> {
11
+ ws: WebSocket
12
+ url: string
13
+
14
+ constructor(url: string) {
15
+ this.ws = new WebSocket(url)
16
+ this.url = url
17
+ }
18
+
19
+ send(data: Schema['body'] | Schema['body'][]) {
20
+ if (Array.isArray(data)) {
21
+ data.forEach((datum) => this.send(datum))
22
+
23
+ return this
24
+ }
25
+
26
+ this.ws.send(
27
+ typeof data === 'object' ? JSON.stringify(data) : data.toString()
28
+ )
29
+
30
+ return this
31
+ }
32
+
33
+ on<K extends keyof WebSocketEventMap>(
34
+ type: K,
35
+ listener: (event: EdenTreaty.WSEvent<K, Schema['response']>) => void,
36
+ options?: boolean | AddEventListenerOptions
37
+ ) {
38
+ return this.addEventListener(type, listener, options)
39
+ }
40
+
41
+ off<K extends keyof WebSocketEventMap>(
42
+ type: K,
43
+ listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
44
+ options?: boolean | EventListenerOptions
45
+ ) {
46
+ this.ws.removeEventListener(type, listener, options)
47
+
48
+ return this
49
+ }
50
+
51
+ subscribe(
52
+ onMessage: (
53
+ event: EdenTreaty.WSEvent<'message', Schema['response']>
54
+ ) => void,
55
+ options?: boolean | AddEventListenerOptions
56
+ ) {
57
+ return this.addEventListener('message', onMessage, options)
58
+ }
59
+
60
+ addEventListener<K extends keyof WebSocketEventMap>(
61
+ type: K,
62
+ listener: (event: EdenTreaty.WSEvent<K, Schema['response']>) => void,
63
+ options?: boolean | AddEventListenerOptions
64
+ ) {
65
+ this.ws.addEventListener(
66
+ type,
67
+ (ws) => {
68
+ if (type === 'message') {
69
+ let data = (ws as MessageEvent).data.toString() as any
70
+ const start = data.charCodeAt(0)
71
+
72
+ if (start === 47 || start === 123)
73
+ try {
74
+ data = JSON.parse(data)
75
+ } catch {}
76
+ else if (!Number.isNaN(+data)) data = +data
77
+ else if (data === 'true') data = true
78
+ else if (data === 'fase') data = false
79
+
80
+ // @ts-ignore
81
+ listener({
82
+ ...ws,
83
+ data
84
+ })
85
+ } else {
86
+ // @ts-ignore
87
+ listener(ws)
88
+ }
89
+ },
90
+ options
91
+ )
92
+
93
+ return this
94
+ }
95
+
96
+ removeEventListener<K extends keyof WebSocketEventMap>(
97
+ type: K,
98
+ listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
99
+ options?: boolean | EventListenerOptions
100
+ ) {
101
+ this.off(type, listener, options)
102
+
103
+ return this
104
+ }
105
+
106
+ close() {
107
+ this.ws.close()
108
+
109
+ return this
110
+ }
111
+ }
112
+
113
+ const createProxy = (
114
+ domain: string,
115
+ path: string = '',
116
+ config: {}
117
+ ): Record<string, unknown> =>
118
+ new Proxy(() => {}, {
119
+ get(target, key, value) {
120
+ return createProxy(domain, `${path}/${key.toString()}`, config)
121
+ },
122
+ apply(
123
+ target,
124
+ _,
125
+ [
126
+ { $query, $fetch, $body, ...bodyObj } = {
127
+ $fetch: undefined,
128
+ $query: undefined,
129
+ $body: undefined
130
+ }
131
+ ]: EdenTreaty.CallOption[] = [{}]
132
+ ) {
133
+ const i = path.lastIndexOf('/'),
134
+ method = path.slice(i + 1),
135
+ url = composePath(domain, path.slice(0, i), $query)
136
+
137
+ if (method === 'subscribe')
138
+ return new EdenWS(
139
+ url.replace(
140
+ /^([^]+):\/\//,
141
+ url.startsWith('https://') ? 'wss://' : 'ws://'
142
+ )
143
+ )
144
+
145
+ const body =
146
+ $body ?? (Object.keys(bodyObj).length ? bodyObj : undefined)
147
+ const isObject = typeof body === 'object'
148
+
149
+ return fetch(url, {
150
+ method,
151
+ body: isObject ? JSON.stringify(body) : body,
152
+ // ...config.fetch,
153
+ ...$fetch,
154
+ headers: body
155
+ ? {
156
+ 'content-type': isObject
157
+ ? 'application/json'
158
+ : 'text/plain',
159
+ // ...config.fetch?.headers,
160
+ ...$fetch?.['headers']
161
+ }
162
+ : undefined
163
+ }).then(async (res) => {
164
+ let data
165
+
166
+ switch (res.headers.get('Content-Type')?.split(';')[0]) {
167
+ case 'application/json':
168
+ data = await res.json()
169
+ break
170
+
171
+ default:
172
+ data = await res.text().then((data) => {
173
+ if (!Number.isNaN(+data)) return +data
174
+ if (data === 'true') return true
175
+ if (data === 'false') return false
176
+
177
+ return data
178
+ })
179
+ }
180
+
181
+ if (res.status > 300)
182
+ return {
183
+ data,
184
+ error: new EdenFetchError(res.status, await data)
185
+ }
186
+
187
+ return { data, error: null }
188
+ })
189
+ }
190
+ }) as unknown as Record<string, unknown>
191
+
192
+ export const edenTreaty = <App extends Elysia<any>>(
193
+ domain: string,
194
+ config: {} = {}
195
+ ): EdenTreaty.Create<App> =>
196
+ new Proxy(
197
+ {},
198
+ {
199
+ get(target, key) {
200
+ return createProxy(domain, key as string, config)
201
+ }
202
+ }
203
+ ) as any
@@ -0,0 +1,195 @@
1
+ import type { Elysia, SCHEMA, AnyTypedSchema } from 'elysia'
2
+
3
+ import type { EdenWS } from './index'
4
+ import type { IsNever, IsUnknown, MapError, UnionToIntersect } from '../types'
5
+ import { EdenFetchError } from '../utils'
6
+
7
+ export namespace EdenTreaty {
8
+ export type Create<App extends Elysia<any>> = App['meta'] extends Record<
9
+ typeof SCHEMA,
10
+ infer Schema extends Record<string, any>
11
+ >
12
+ ? EdenTreaty.Sign<Schema>
13
+ : 'Please install Elysia before using Eden'
14
+
15
+ export interface Config {}
16
+
17
+ export type Sign<A> = {
18
+ [Path in keyof A as Path extends `/${infer Prefix}/${infer _}`
19
+ ? Prefix
20
+ : Path extends `/`
21
+ ? 'index'
22
+ : Path extends `/${infer Prefix}`
23
+ ? Prefix
24
+ : never]: UnionToIntersect<
25
+ Path extends `/${infer _}/${infer Rest}`
26
+ ? NestPath<
27
+ Rest,
28
+ {
29
+ [Method in keyof A[Path]]: A[Path][Method] extends infer Route extends AnyTypedSchema
30
+ ? Method extends 'subscribe'
31
+ ? undefined extends Route['query']
32
+ ? (params?: {
33
+ $query?: Record<string, string>
34
+ }) => EdenWS<Route>
35
+ : undefined extends Route['query']
36
+ ? (params: {
37
+ $query: Route['query']
38
+ }) => EdenWS<Route>
39
+ : (params?: {
40
+ $query?: Record<string, string>
41
+ }) => EdenWS<Route>
42
+ : IsUnknown<Route['body']> extends true
43
+ ? (params?: {
44
+ $query?: Record<string, string>
45
+ $fetch?: RequestInit
46
+ }) => Promise<
47
+ | {
48
+ data: Route['response']['200']
49
+ error: null
50
+ }
51
+ | {
52
+ data: null
53
+ error: MapError<
54
+ Route['response']
55
+ > extends infer Errors
56
+ ? IsNever<Errors> extends true
57
+ ? EdenFetchError<
58
+ number,
59
+ string
60
+ >
61
+ : Errors
62
+ : EdenFetchError<
63
+ number,
64
+ string
65
+ >
66
+ }
67
+ >
68
+ : (
69
+ params: Route['body'] & {
70
+ $query?: Record<string, string>
71
+ $fetch?: RequestInit
72
+ }
73
+ ) => Promise<
74
+ | {
75
+ data: Route['response'] extends {
76
+ 200: infer ReturnedType
77
+ }
78
+ ? ReturnedType
79
+ : unknown
80
+ error: null
81
+ }
82
+ | {
83
+ data: null
84
+ error: MapError<
85
+ Route['response']
86
+ > extends infer Errors
87
+ ? IsNever<Errors> extends true
88
+ ? EdenFetchError<
89
+ number,
90
+ string
91
+ >
92
+ : Errors
93
+ : EdenFetchError<
94
+ number,
95
+ string
96
+ >
97
+ }
98
+ >
99
+ : never
100
+ }
101
+ >
102
+ : {
103
+ [Method in keyof A[Path]]: A[Path][Method] extends infer Route extends AnyTypedSchema
104
+ ? Method extends 'subscribe'
105
+ ? IsUnknown<Route['query']> extends true
106
+ ? (params?: {
107
+ $query?: Record<string, string>
108
+ }) => EdenWS<Route>
109
+ : undefined extends Route['query']
110
+ ? (params: {
111
+ $query: Route['query']
112
+ }) => EdenWS<Route>
113
+ : (params?: {
114
+ $query?: Record<string, string>
115
+ }) => EdenWS<Route>
116
+ : IsUnknown<Route['body']> extends true
117
+ ? (params?: {
118
+ $query?: Record<string, string>
119
+ $fetch?: RequestInit
120
+ }) => Promise<
121
+ | {
122
+ data: Route['response']['200']
123
+ error: null
124
+ }
125
+ | {
126
+ data: null
127
+ error: MapError<
128
+ Route['response']
129
+ > extends infer Errors
130
+ ? IsNever<Errors> extends true
131
+ ? EdenFetchError<
132
+ number,
133
+ string
134
+ >
135
+ : Errors
136
+ : EdenFetchError<number, string>
137
+ }
138
+ >
139
+ : (
140
+ params: Route['body'] & {
141
+ $query?: Record<string, string>
142
+ $fetch?: RequestInit
143
+ }
144
+ ) => Promise<
145
+ | {
146
+ data: Route['response'] extends {
147
+ 200: infer ReturnedType
148
+ }
149
+ ? ReturnedType
150
+ : unknown
151
+ error: null
152
+ }
153
+ | {
154
+ data: null
155
+ error: MapError<
156
+ Route['response']
157
+ > extends infer Errors
158
+ ? IsNever<Errors> extends true
159
+ ? EdenFetchError<
160
+ number,
161
+ string
162
+ >
163
+ : Errors
164
+ : EdenFetchError<number, string>
165
+ }
166
+ >
167
+ : never
168
+ }
169
+ >
170
+ }
171
+
172
+ export interface OnMessage<Data = unknown> extends MessageEvent {
173
+ data: Data
174
+ rawData: MessageEvent['data']
175
+ }
176
+
177
+ export type WSEvent<
178
+ K extends keyof WebSocketEventMap,
179
+ Data = unknown
180
+ > = K extends 'message' ? OnMessage<Data> : WebSocketEventMap[K]
181
+
182
+ export interface CallOption {
183
+ [x: string]: any
184
+ $fetch?: RequestInit
185
+ $query?: Record<string, string>
186
+ }
187
+ }
188
+
189
+ type NestPath<T extends string, V> = T extends `${infer First}/${infer Rest}`
190
+ ? First extends `:${infer Parameter}`
191
+ ? Record<string | number | `:${Parameter}`, NestPath<Rest, V>>
192
+ : Record<First, NestPath<Rest, V>>
193
+ : T extends `:${infer Parameter}`
194
+ ? Record<string | number | T, V>
195
+ : Record<T, V>