spiceflow 0.0.7 → 1.0.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.
Files changed (151) hide show
  1. package/README.md +1 -171
  2. package/dist/client/errors.d.ts +7 -0
  3. package/dist/client/errors.d.ts.map +1 -0
  4. package/dist/client/errors.js +18 -0
  5. package/dist/client/errors.js.map +1 -0
  6. package/dist/client/index.d.ts +15 -0
  7. package/dist/client/index.d.ts.map +1 -0
  8. package/dist/client/index.js +376 -0
  9. package/dist/client/index.js.map +1 -0
  10. package/dist/client/types.d.ts +87 -0
  11. package/dist/client/types.d.ts.map +1 -0
  12. package/dist/client/types.js +2 -0
  13. package/dist/client/types.js.map +1 -0
  14. package/dist/client/utils.d.ts +2 -0
  15. package/dist/client/utils.d.ts.map +1 -0
  16. package/dist/client/utils.js +9 -0
  17. package/dist/client/utils.js.map +1 -0
  18. package/dist/client/ws.d.ts +15 -0
  19. package/dist/client/ws.d.ts.map +1 -0
  20. package/dist/client/ws.js +51 -0
  21. package/dist/client/ws.js.map +1 -0
  22. package/dist/client.test.d.ts +2 -0
  23. package/dist/client.test.d.ts.map +1 -0
  24. package/dist/client.test.js +237 -0
  25. package/dist/client.test.js.map +1 -0
  26. package/dist/elysia-fork/context.d.ts +87 -0
  27. package/dist/elysia-fork/context.d.ts.map +1 -0
  28. package/dist/elysia-fork/context.js +2 -0
  29. package/dist/elysia-fork/context.js.map +1 -0
  30. package/dist/elysia-fork/error.d.ts +246 -0
  31. package/dist/elysia-fork/error.d.ts.map +1 -0
  32. package/dist/elysia-fork/error.js +195 -0
  33. package/dist/elysia-fork/error.js.map +1 -0
  34. package/dist/elysia-fork/types.d.ts +570 -0
  35. package/dist/elysia-fork/types.d.ts.map +1 -0
  36. package/dist/elysia-fork/types.js +3 -0
  37. package/dist/elysia-fork/types.js.map +1 -0
  38. package/dist/elysia-fork/utils.d.ts +134 -0
  39. package/dist/elysia-fork/utils.d.ts.map +1 -0
  40. package/dist/elysia-fork/utils.js +70 -0
  41. package/dist/elysia-fork/utils.js.map +1 -0
  42. package/dist/index.d.ts +2 -7
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -22
  45. package/dist/index.js.map +1 -1
  46. package/dist/spiceflow.d.ts +237 -0
  47. package/dist/spiceflow.d.ts.map +1 -0
  48. package/dist/spiceflow.js +484 -0
  49. package/dist/spiceflow.js.map +1 -0
  50. package/dist/spiceflow.test.d.ts +2 -0
  51. package/dist/spiceflow.test.d.ts.map +1 -0
  52. package/dist/spiceflow.test.js +225 -0
  53. package/dist/spiceflow.test.js.map +1 -0
  54. package/dist/stream.test.d.ts +2 -0
  55. package/dist/stream.test.d.ts.map +1 -0
  56. package/dist/stream.test.js +286 -0
  57. package/dist/stream.test.js.map +1 -0
  58. package/dist/types.d.ts +1 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils.d.ts +4 -20
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +17 -46
  65. package/dist/utils.js.map +1 -1
  66. package/package.json +12 -37
  67. package/src/client/errors.ts +21 -0
  68. package/src/client/index.ts +541 -0
  69. package/src/client/types.ts +233 -0
  70. package/src/client/utils.ts +7 -0
  71. package/src/client/ws.ts +99 -0
  72. package/src/client.test.ts +235 -0
  73. package/src/elysia-fork/context.ts +196 -0
  74. package/src/elysia-fork/error.ts +293 -0
  75. package/src/elysia-fork/types.ts +1353 -0
  76. package/src/elysia-fork/utils.ts +85 -0
  77. package/src/index.ts +2 -34
  78. package/src/spiceflow.test.ts +290 -0
  79. package/src/spiceflow.ts +1251 -0
  80. package/src/stream.test.ts +342 -0
  81. package/src/types.ts +0 -0
  82. package/src/utils.ts +21 -70
  83. package/context.d.ts +0 -2
  84. package/context.js +0 -1
  85. package/dist/babel.test.d.ts +0 -2
  86. package/dist/babel.test.d.ts.map +0 -1
  87. package/dist/babel.test.js +0 -27
  88. package/dist/babel.test.js.map +0 -1
  89. package/dist/babelDebugOutputs.d.ts +0 -9
  90. package/dist/babelDebugOutputs.d.ts.map +0 -1
  91. package/dist/babelDebugOutputs.js +0 -34
  92. package/dist/babelDebugOutputs.js.map +0 -1
  93. package/dist/babelTransformRpc.d.ts +0 -19
  94. package/dist/babelTransformRpc.d.ts.map +0 -1
  95. package/dist/babelTransformRpc.js +0 -285
  96. package/dist/babelTransformRpc.js.map +0 -1
  97. package/dist/browser.d.ts +0 -8
  98. package/dist/browser.d.ts.map +0 -1
  99. package/dist/browser.js +0 -126
  100. package/dist/browser.js.map +0 -1
  101. package/dist/build.d.ts +0 -13
  102. package/dist/build.d.ts.map +0 -1
  103. package/dist/build.js +0 -230
  104. package/dist/build.js.map +0 -1
  105. package/dist/cli.d.ts +0 -3
  106. package/dist/cli.d.ts.map +0 -1
  107. package/dist/cli.js +0 -111
  108. package/dist/cli.js.map +0 -1
  109. package/dist/context-internal.d.ts +0 -20
  110. package/dist/context-internal.d.ts.map +0 -1
  111. package/dist/context-internal.js +0 -16
  112. package/dist/context-internal.js.map +0 -1
  113. package/dist/context.d.ts +0 -2
  114. package/dist/context.d.ts.map +0 -1
  115. package/dist/context.js +0 -2
  116. package/dist/context.js.map +0 -1
  117. package/dist/expose.d.ts +0 -6
  118. package/dist/expose.d.ts.map +0 -1
  119. package/dist/expose.js +0 -32
  120. package/dist/expose.js.map +0 -1
  121. package/dist/headers.d.ts +0 -2
  122. package/dist/headers.d.ts.map +0 -1
  123. package/dist/headers.js +0 -18
  124. package/dist/headers.js.map +0 -1
  125. package/dist/jsonRpc.d.ts +0 -32
  126. package/dist/jsonRpc.d.ts.map +0 -1
  127. package/dist/jsonRpc.js +0 -3
  128. package/dist/jsonRpc.js.map +0 -1
  129. package/dist/server.d.ts +0 -32
  130. package/dist/server.d.ts.map +0 -1
  131. package/dist/server.js +0 -292
  132. package/dist/server.js.map +0 -1
  133. package/headers.d.ts +0 -2
  134. package/headers.js +0 -1
  135. package/sdk-template/package.json +0 -22
  136. package/sdk-template/src/index.ts +0 -2
  137. package/sdk-template/src/v1/example.ts +0 -5
  138. package/sdk-template/src/v1/generator.ts +0 -12
  139. package/sdk-template/tsconfig.json +0 -16
  140. package/src/babel.test.ts +0 -35
  141. package/src/babelDebugOutputs.ts +0 -56
  142. package/src/babelTransformRpc.ts +0 -394
  143. package/src/browser.ts +0 -141
  144. package/src/build.ts +0 -298
  145. package/src/cli.ts +0 -132
  146. package/src/context-internal.ts +0 -36
  147. package/src/context.ts +0 -5
  148. package/src/expose.ts +0 -34
  149. package/src/headers.ts +0 -19
  150. package/src/jsonRpc.ts +0 -43
  151. package/src/server.ts +0 -384
@@ -0,0 +1,196 @@
1
+ import type {
2
+ StatusMap,
3
+ InvertedStatusMap,
4
+ redirect as Redirect
5
+ } from './utils.js'
6
+
7
+ import type {
8
+ RouteSchema,
9
+ Prettify,
10
+ ResolvePath,
11
+ SingletonBase,
12
+ HTTPHeaders
13
+ } from './types.js'
14
+ import { error } from './error.js'
15
+
16
+ type InvertedStatusMapKey = keyof InvertedStatusMap
17
+
18
+ // type WithoutNullableKeys<Type> = {
19
+ // [Key in keyof Type]-?: NonNullable<Type[Key]>
20
+ // }
21
+
22
+ export type ErrorContext<
23
+ in out Route extends RouteSchema = {},
24
+ in out Singleton extends SingletonBase = {
25
+ decorator: {}
26
+ store: {}
27
+ derive: {}
28
+ resolve: {}
29
+ },
30
+ Path extends string = ''
31
+ > = Prettify<
32
+ {
33
+ body: Route['body']
34
+ query: undefined extends Route['query']
35
+ ? Record<string, string | undefined>
36
+ : Route['query']
37
+ params: undefined extends Route['params']
38
+ ? Path extends `${string}/${':' | '*'}${string}`
39
+ ? ResolvePath<Path>
40
+ : { [key in string]: string }
41
+ : Route['params']
42
+
43
+ // server: Server | null
44
+ redirect: Redirect
45
+
46
+ /**
47
+ * Path extracted from incoming URL
48
+ *
49
+ * Represent a value extracted from URL
50
+ *
51
+ * @example '/id/9'
52
+ */
53
+ path: string
54
+ /**
55
+ * Path as registered to router
56
+ *
57
+ * Represent a path registered to a router, not a URL
58
+ *
59
+ * @example '/id/:id'
60
+ */
61
+ route: string
62
+ request: Request
63
+ store: Singleton['store']
64
+ response: Route['response']
65
+ } & Singleton['decorator'] &
66
+ Singleton['derive'] &
67
+ Singleton['resolve']
68
+ >
69
+
70
+ export type Context<
71
+ in out Route extends RouteSchema = {},
72
+ in out Singleton extends SingletonBase = {
73
+ decorator: {}
74
+ store: {}
75
+ derive: {}
76
+ resolve: {}
77
+ },
78
+ Path extends string = ''
79
+ > = Prettify<
80
+ {
81
+ body: Route['body']
82
+
83
+ params: undefined extends Route['params']
84
+ ? Path extends `${string}/${':' | '*'}${string}`
85
+ ? ResolvePath<Path>
86
+ : never
87
+ : Route['params']
88
+
89
+ // server: Server | null
90
+ redirect: Redirect
91
+
92
+ // set: {
93
+ // headers: HTTPHeaders
94
+ // status?: number | keyof StatusMap
95
+ // /**
96
+ // * @deprecated Use inline redirect instead
97
+ // *
98
+ // * Will be removed in 1.2.0
99
+ // *
100
+ // * @example Migration example
101
+ // * ```ts
102
+ // * new Spiceflow()
103
+ // * .get(({ redirect }) => redirect('/'))
104
+ // * ```
105
+ // */
106
+ // redirect?: string
107
+ // /**
108
+ // * ! Internal Property
109
+ // *
110
+ // * Use `Context.cookie` instead
111
+ // */
112
+ // cookie?: Record<string, SpiceflowCookie>
113
+ // }
114
+
115
+ /**
116
+ * Path extracted from incoming URL
117
+ *
118
+ * Represent a value extracted from URL
119
+ *
120
+ * @example '/id/9'
121
+ */
122
+ path: string
123
+ /**
124
+ * Path as registered to router
125
+ *
126
+ * Represent a path registered to a router, not a URL
127
+ *
128
+ * @example '/id/:id'
129
+ */
130
+ route: string
131
+ request: Request
132
+ store: Singleton['store']
133
+ response?: Route['response']
134
+ } & ({} extends Route['response']
135
+ ? {
136
+ error: typeof error
137
+ }
138
+ : {
139
+ error: <
140
+ const Code extends
141
+ | keyof Route['response']
142
+ | InvertedStatusMap[Extract<
143
+ InvertedStatusMapKey,
144
+ keyof Route['response']
145
+ >],
146
+ const T extends Code extends keyof Route['response']
147
+ ? Route['response'][Code]
148
+ : Code extends keyof StatusMap
149
+ ? // @ts-ignore StatusMap[Code] always valid because Code generic check
150
+ Route['response'][StatusMap[Code]]
151
+ : never
152
+ >(
153
+ code: Code,
154
+ response: T
155
+ ) => {
156
+ // [ELYSIA_RESPONSE]: Code extends keyof StatusMap
157
+ // ? StatusMap[Code]
158
+ // : Code
159
+ response: T
160
+ _type: {
161
+ [ERROR_CODE in Code extends keyof StatusMap
162
+ ? StatusMap[Code]
163
+ : Code]: T
164
+ }
165
+ }
166
+ }) &
167
+ Singleton['decorator'] &
168
+ Singleton['derive'] &
169
+ Singleton['resolve']
170
+ >
171
+
172
+ // Use to mimic request before mapping route
173
+ export type PreContext<
174
+ in out Singleton extends SingletonBase = {
175
+ decorator: {}
176
+ store: {}
177
+ derive: {}
178
+ resolve: {}
179
+ }
180
+ > = Prettify<
181
+ {
182
+ store: Singleton['store']
183
+ request: Request
184
+
185
+ redirect: Redirect
186
+ // server: Server | null
187
+
188
+ // set: {
189
+ // headers: HTTPHeaders
190
+ // status?: number
191
+ // redirect?: string
192
+ // }
193
+
194
+ error: typeof error
195
+ } & Singleton['decorator']
196
+ >
@@ -0,0 +1,293 @@
1
+ import type { TSchema } from '@sinclair/typebox'
2
+ import { Value } from '@sinclair/typebox/value'
3
+ import type { TypeCheck, ValueError } from '@sinclair/typebox/compiler'
4
+
5
+ import { StatusMap, InvertedStatusMap } from './utils'
6
+
7
+ // ? Cloudflare worker support
8
+ const env =
9
+ // @ts-ignore
10
+ typeof Bun !== 'undefined'
11
+ ? // @ts-ignore
12
+ Bun.env
13
+ : typeof process !== 'undefined'
14
+ ? process?.env
15
+ : undefined
16
+
17
+ export const ERROR_CODE = Symbol('SpiceflowErrorCode')
18
+ export type ERROR_CODE = typeof ERROR_CODE
19
+
20
+ export const ELYSIA_RESPONSE = Symbol('SpiceflowResponse')
21
+ export type ELYSIA_RESPONSE = typeof ELYSIA_RESPONSE
22
+
23
+ export const isProduction = (env?.NODE_ENV ?? env?.ENV) === 'production'
24
+
25
+ export type SpiceflowErrors =
26
+ | InternalServerError
27
+ | NotFoundError
28
+ | ParseError
29
+ | ValidationError
30
+ | InvalidCookieSignature
31
+
32
+ export const error = <
33
+ const Code extends number | keyof StatusMap,
34
+ const T = Code extends keyof InvertedStatusMap
35
+ ? InvertedStatusMap[Code]
36
+ : Code,
37
+ const Status extends Code extends keyof StatusMap
38
+ ? StatusMap[Code]
39
+ : Code = Code extends keyof StatusMap ? StatusMap[Code] : Code
40
+ >(
41
+ code: Code,
42
+ response?: T
43
+ ): {
44
+ [ELYSIA_RESPONSE]: Status
45
+ response: T
46
+ _type: {
47
+ [ERROR_CODE in Status]: T
48
+ }
49
+ error: Error
50
+ } => {
51
+ const res =
52
+ response ??
53
+ (code in InvertedStatusMap
54
+ ? // @ts-expect-error Always correct
55
+ InvertedStatusMap[code]
56
+ : code)
57
+
58
+ return {
59
+ // @ts-expect-error trust me bro
60
+ [ELYSIA_RESPONSE]: StatusMap[code] ?? code,
61
+ response: res,
62
+ _type: undefined as any,
63
+ error: new Error(res)
64
+ } as const
65
+ }
66
+
67
+ export class InternalServerError extends Error {
68
+ code = 'INTERNAL_SERVER_ERROR'
69
+ status = 500
70
+
71
+ constructor(message?: string) {
72
+ super(message ?? 'INTERNAL_SERVER_ERROR')
73
+ }
74
+ }
75
+
76
+ export class NotFoundError extends Error {
77
+ code = 'NOT_FOUND'
78
+ status = 404
79
+
80
+ constructor(message?: string) {
81
+ super(message ?? 'NOT_FOUND')
82
+ }
83
+ }
84
+
85
+ export class ParseError extends Error {
86
+ code = 'PARSE'
87
+ status = 400
88
+
89
+ constructor() {
90
+ super('Failed to parse body')
91
+ }
92
+ }
93
+
94
+ export class InvalidCookieSignature extends Error {
95
+ code = 'INVALID_COOKIE_SIGNATURE'
96
+ status = 400
97
+
98
+ constructor(public key: string, message?: string) {
99
+ super(message ?? `"${key}" has invalid cookie signature`)
100
+ }
101
+ }
102
+
103
+ export const mapValueError = (error: ValueError) => {
104
+ const { message, path, value, type } = error
105
+
106
+ const property = path.slice(1).replaceAll('/', '.')
107
+ const isRoot = path === ''
108
+
109
+ switch (type) {
110
+ case 42:
111
+ return {
112
+ ...error,
113
+ summary: isRoot
114
+ ? `Value should not be provided`
115
+ : `Property '${property}' should not be provided`
116
+ }
117
+
118
+ case 45:
119
+ return {
120
+ ...error,
121
+ summary: isRoot
122
+ ? `Value is missing`
123
+ : `Property '${property}' is missing`
124
+ }
125
+
126
+ case 50:
127
+ // Expected string to match 'email' format
128
+ const quoteIndex = message.indexOf("'")!
129
+ const format = message.slice(
130
+ quoteIndex + 1,
131
+ message.indexOf("'", quoteIndex + 1)
132
+ )
133
+
134
+ return {
135
+ ...error,
136
+ summary: isRoot
137
+ ? `Value should be an email`
138
+ : `Property '${property}' should be ${format}`
139
+ }
140
+
141
+ case 54:
142
+ return {
143
+ ...error,
144
+ summary: `${message.slice(
145
+ 0,
146
+ 9
147
+ )} property '${property}' to be ${message.slice(
148
+ 8
149
+ )} but found: ${value}`
150
+ }
151
+
152
+ case 62:
153
+ const union = error.schema.anyOf
154
+ .map((x: Record<string, unknown>) => `'${x?.format ?? x.type}'`)
155
+ .join(', ')
156
+
157
+ return {
158
+ ...error,
159
+ summary: isRoot
160
+ ? `Value should be one of ${union}`
161
+ : `Property '${property}' should be one of: ${union}`
162
+ }
163
+
164
+ default:
165
+ return { summary: message, ...error }
166
+ }
167
+ }
168
+
169
+ export class ValidationError extends Error {
170
+ code = 'VALIDATION'
171
+ status = 422
172
+
173
+ constructor(
174
+ public type: string,
175
+ public validator: TSchema | TypeCheck<any>,
176
+ public value: unknown
177
+ ) {
178
+ if (value && typeof value === 'object' && ELYSIA_RESPONSE in value)
179
+ // @ts-expect-error
180
+ value = value.response
181
+
182
+ const error = isProduction
183
+ ? undefined
184
+ : 'Errors' in validator
185
+ ? validator.Errors(value).First()
186
+ : Value.Errors(validator, value).First()
187
+
188
+ const customError =
189
+ error?.schema.error !== undefined
190
+ ? typeof error.schema.error === 'function'
191
+ ? error.schema.error({
192
+ type,
193
+ validator,
194
+ value,
195
+ get errors() {
196
+ return [...validator.Errors(value)].map(
197
+ mapValueError
198
+ )
199
+ }
200
+ })
201
+ : error.schema.error
202
+ : undefined
203
+
204
+ const accessor = error?.path || 'root'
205
+ let message = ''
206
+
207
+ if (customError !== undefined) {
208
+ message =
209
+ typeof customError === 'object'
210
+ ? JSON.stringify(customError)
211
+ : customError + ''
212
+ } else if (isProduction) {
213
+ message = JSON.stringify({
214
+ type: 'validation',
215
+ on: type,
216
+ summary: mapValueError(error).summary,
217
+ message: error?.message,
218
+ found: value
219
+ })
220
+ } else {
221
+ // @ts-ignore private field
222
+ const schema = validator?.schema ?? validator
223
+ const errors =
224
+ 'Errors' in validator
225
+ ? [...validator.Errors(value)].map(mapValueError)
226
+ : [...Value.Errors(validator, value)].map(mapValueError)
227
+
228
+ let expected
229
+
230
+ try {
231
+ expected = Value.Create(schema)
232
+ } catch (error) {
233
+ expected = {
234
+ type: 'Could not create expected value',
235
+ // @ts-expect-error
236
+ message: error?.message,
237
+ error
238
+ }
239
+ }
240
+
241
+ message = JSON.stringify(
242
+ {
243
+ type: 'validation',
244
+ on: type,
245
+ summary: errors[0]?.summary,
246
+ property: accessor,
247
+ message: error?.message,
248
+ expected,
249
+ found: value,
250
+ errors
251
+ },
252
+ null,
253
+ 2
254
+ )
255
+ }
256
+
257
+ super(message)
258
+
259
+ Object.setPrototypeOf(this, ValidationError.prototype)
260
+ }
261
+
262
+ get all() {
263
+ return 'Errors' in this.validator
264
+ ? [...this.validator.Errors(this.value)].map(mapValueError)
265
+ : // @ts-ignore
266
+ [...Value.Errors(this.validator, this.value)].map(mapValueError)
267
+ }
268
+
269
+ static simplifyModel(validator: TSchema | TypeCheck<any>) {
270
+ // @ts-ignore
271
+ const model = 'schema' in validator ? validator.schema : validator
272
+
273
+ try {
274
+ return Value.Create(model)
275
+ } catch {
276
+ return model
277
+ }
278
+ }
279
+
280
+ get model() {
281
+ return ValidationError.simplifyModel(this.validator)
282
+ }
283
+
284
+ toResponse(headers?: Record<string, any>) {
285
+ return new Response(this.message, {
286
+ status: 400,
287
+ headers: {
288
+ ...headers,
289
+ 'content-type': 'application/json'
290
+ }
291
+ })
292
+ }
293
+ }