@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.
@@ -0,0 +1,15 @@
1
+ export const composePath = (
2
+ domain: string,
3
+ path: string,
4
+ query: Record<string, string> | undefined
5
+ ) => {
6
+ if (!domain.endsWith('/')) domain += '/'
7
+ if (path === 'index') path = ''
8
+
9
+ if (!query || !Object.keys(query).length) return `${domain}${path}`
10
+
11
+ let q = ''
12
+ for (const [key, value] of Object.entries(query)) q += `${key}=${value}&`
13
+
14
+ return `${domain}${path}?${q.slice(0, -1)}`
15
+ }
package/src/types.ts CHANGED
@@ -1,204 +1,18 @@
1
1
  import type {
2
2
  Elysia,
3
3
  SCHEMA,
4
- TypedRoute,
5
4
  IsPathParameter,
6
- EXPOSED
5
+ EXPOSED,
6
+ AnyTypedSchema
7
7
  } from 'elysia'
8
- import type { TObject } from '@sinclair/typebox'
9
8
 
10
- import type { EdenWS } from '.'
11
- import { type EdenFetchError, type Signal } from './utils'
9
+ import type { EdenFetchError } from './utils'
12
10
 
13
- type IsAny<T> = unknown extends T
14
- ? [T] extends [object]
15
- ? true
16
- : false
17
- : false
18
-
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 {
48
- [key in typeof SCHEMA]: infer Schema extends Record<
49
- string,
50
- Record<string, TypedRoute>
51
- >
52
- }
53
- ? UnionToIntersection<CreateEden<Schema>> & {
54
- $fn: CreateEdenFn<App['meta'][typeof EXPOSED]>
55
- }
56
- : 'Please install Elysia before using Eden'
57
-
58
- export interface EdenCall {
59
- [x: string]: any
60
- $fetch?: RequestInit
61
- $query?: Record<string, string | boolean | number>
62
- }
63
-
64
- export type UnionToIntersection<U> = (
65
- U extends any ? (k: U) => void : never
66
- ) extends (k: infer I) => void
67
- ? I
68
- : never
69
-
70
- // https://twitter.com/mattpocockuk/status/1622730173446557697?s=20
71
- type Prettify<T> = {
72
- [K in keyof T]: T[K]
73
- } & {}
74
-
75
- type TypedRouteToParams<Route extends TypedRoute> =
76
- (Route['body'] extends NonNullable<Route['body']>
77
- ? Route['body'] extends Record<any, any>
78
- ? Route['body']
79
- : {
80
- $body: Route['body']
81
- }
82
- : {}) &
83
- (Route['query'] extends NonNullable<Route['query']>
84
- ? unknown extends Route['query']
85
- ? {}
86
- : {
87
- $query: Route['query']
88
- }
89
- : {})
90
-
91
- export type CreateEden<
92
- Server extends Record<string, Record<string, TypedRoute>>,
93
- // pathnames are always string
94
- Path extends string = keyof Server extends string ? keyof Server : never,
95
- Full extends string = ''
96
- > = Path extends `/${infer Start}`
97
- ? CreateEden<Server, Start, Path>
98
- : Path extends `${infer A}/${infer B}`
99
- ? // If path parameters, accept any string
100
- IsPathParameter<A> extends never
101
- ? {
102
- [key in A]: CreateEden<Server, B, Full>
103
- }
104
- : Record<string, CreateEden<Server, B, Full>> &
105
- Record<
106
- `$${A}`,
107
- `Expected path parameters ':${A}', replace this with any string`
108
- >
109
- : // Iterate until last string then catch method
110
- {
111
- [key in Path extends ''
112
- ? // If end with empty, then return as index
113
- 'index'
114
- : Path extends `:${infer params}`
115
- ? string
116
- : Path | CamelCase<Path>]: Full extends keyof Server
117
- ? {
118
- // Check if is method
119
- [key in keyof Server[Full] extends string
120
- ? Lowercase<keyof Server[Full]>
121
- : keyof Server[Full]]: [
122
- Server[Full][key extends string ? Uppercase<key> : key]
123
- ] extends [infer Route extends TypedRoute]
124
- ? undefined extends Route['body']
125
- ? (params?: {
126
- $query?: EdenCall['$query']
127
- $fetch?: EdenCall['$fetch']
128
- }) => Promise<
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']>
151
- : unknown
152
- >
153
- : key extends 'subscribe'
154
- ? // Since subscribe key is only a lower letter
155
- [
156
- Server[Full][key],
157
- Server[Full][key]['query']
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
173
- }
174
- : never
175
- } & (Path extends `:${infer params}`
176
- ? Record<
177
- `$${params}`,
178
- `Expected path parameters ':${params}', replace this with any string`
179
- >
180
- : {})
181
-
182
- // https://stackoverflow.com/questions/59623524/typescript-how-to-map-type-keys-to-camelcase
183
- type CamelCase<S extends string> =
184
- S extends `${infer P1}-${infer P2}${infer P3}`
185
- ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
186
- : Lowercase<S>
187
-
188
- export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
189
- data: Data
190
- rawData: MessageEvent['data']
191
- }
192
-
193
- export type EdenWSEvent<
194
- K extends keyof WebSocketEventMap,
195
- Data = unknown
196
- > = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K]
197
-
198
- export interface EdenConfig {
199
- fn?: string
200
- fetch?: Omit<RequestInit, 'body'>
201
- }
11
+ // https://stackoverflow.com/a/39495173
12
+ type Range<F extends number, T extends number> = Exclude<
13
+ Enumerate<T>,
14
+ Enumerate<F>
15
+ >
202
16
 
203
17
  type Enumerate<
204
18
  N extends number,
@@ -207,15 +21,9 @@ type Enumerate<
207
21
  ? Acc[number]
208
22
  : Enumerate<N, [...Acc, Acc['length']]>
209
23
 
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
24
  type ErrorRange = Range<300, 599>
217
25
 
218
- type MapError<T extends Record<number, unknown>> = [
26
+ export type MapError<T extends Record<number, unknown>> = [
219
27
  {
220
28
  [K in keyof T]-?: K extends ErrorRange ? K : never
221
29
  }[keyof T]
@@ -224,3 +32,34 @@ type MapError<T extends Record<number, unknown>> = [
224
32
  [K in A]: EdenFetchError<K, T[K]>
225
33
  }[A]
226
34
  : false
35
+
36
+ // https://twitter.com/mattpocockuk/status/1622730173446557697?s=20
37
+ type Prettify<T> = {
38
+ [K in keyof T]: T[K]
39
+ } & {}
40
+
41
+ export type UnionToIntersect<U> = (
42
+ U extends unknown ? (arg: U) => 0 : never
43
+ ) extends (arg: infer I) => 0
44
+ ? I
45
+ : never
46
+
47
+ export type IsAny<T> = 0 extends 1 & T ? true : false
48
+
49
+ export type IsNever<T> = [T] extends [never] ? true : false
50
+
51
+ export type IsUnknown<T> = IsAny<T> extends true
52
+ ? false
53
+ : unknown extends T
54
+ ? true
55
+ : false
56
+
57
+ export type AnyTypedRoute = {
58
+ body: unknown
59
+ headers: Record<string, any> | undefined
60
+ query: Record<string, any> | undefined
61
+ params: Record<string, any> | undefined
62
+ response: Record<string, unknown> & {
63
+ '200': unknown
64
+ }
65
+ }
package/src/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { serialize, deserialize } from 'superjson'
2
- import type { EdenCall, EdenConfig } from './types'
3
2
 
4
3
  export class EdenFetchError<
5
4
  Status extends number = number,
@@ -15,107 +14,3 @@ export class EdenFetchError<
15
14
  this.value = value
16
15
  }
17
16
  }
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
- }
package/dist/index.umd.js DELETED
@@ -1 +0,0 @@
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"})});