spiceflow 1.10.1 → 1.11.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/README.md CHANGED
@@ -37,16 +37,26 @@ Objects returned from route handlers are automatically serialized to JSON
37
37
  import { Spiceflow } from 'spiceflow'
38
38
 
39
39
  const app = new Spiceflow()
40
- .get('/hello', () => 'Hello, World!')
41
- .post('/echo', async ({ request }) => {
42
- const body = await request.json()
43
- return { echo: body }
40
+ .route({
41
+ method: 'POST',
42
+ path: '/hello',
43
+ handler() {
44
+ return 'Hello, World!'
45
+ },
46
+ })
47
+ .route({
48
+ method: 'POST',
49
+ path: '/echo',
50
+ async handler({ request }) {
51
+ const body = await request.json()
52
+ return { echo: body }
53
+ },
44
54
  })
45
55
 
46
56
  app.listen(3000)
47
57
  ```
48
58
 
49
- > Never declare app and add routes separately, that way you lose the type safety. Instead always append routes with .post and .get in a single expression.
59
+ > Never declare app and add routes separately, that way you lose the type safety. Instead always append routes with .route in a single expression.
50
60
 
51
61
  ```ts
52
62
  // This is an example of what NOT to do when using Spiceflow
@@ -57,10 +67,20 @@ import { Spiceflow } from 'spiceflow'
57
67
  const app = new Spiceflow()
58
68
 
59
69
  // Do NOT do this! Adding routes separately like this will lose type safety
60
- app.get('/hello', () => 'Hello, World!')
61
- app.post('/echo', async ({ request }) => {
62
- const body = await request.json()
63
- return body
70
+ app.route({
71
+ method: 'GET',
72
+ path: '/hello',
73
+ handler() {
74
+ return 'Hello, World!'
75
+ },
76
+ })
77
+ app.route({
78
+ method: 'POST',
79
+ path: '/echo',
80
+ async handler({ request }) {
81
+ const body = await request.json()
82
+ return body
83
+ },
64
84
  })
65
85
  ```
66
86
 
@@ -93,19 +113,18 @@ This project shares many inspirations with Hono with many differences
93
113
  import { z } from 'zod'
94
114
  import { Spiceflow } from 'spiceflow'
95
115
 
96
- new Spiceflow().post(
97
- '/users',
98
- async ({ request }) => {
116
+ new Spiceflow().route({
117
+ method: 'POST',
118
+ path: '/users',
119
+ request: z.object({
120
+ name: z.string(),
121
+ email: z.string().email(),
122
+ }),
123
+ async handler({ request }) {
99
124
  const body = await request.json() // here body has type { name: string, email: string }
100
125
  return `Created user: ${body.name}`
101
126
  },
102
- {
103
- body: z.object({
104
- name: z.string(),
105
- email: z.string().email(),
106
- }),
107
- },
108
- )
127
+ })
109
128
  ```
110
129
 
111
130
  > Notice that to get the body of the request, you need to call `request.json()` to parse the body as JSON.
@@ -117,25 +136,24 @@ new Spiceflow().post(
117
136
  import { z } from 'zod'
118
137
  import { Spiceflow } from 'spiceflow'
119
138
 
120
- new Spiceflow().get(
121
- '/users/:id',
122
- ({ request, params }) => {
139
+ new Spiceflow().route({
140
+ method: 'GET',
141
+ path: '/users/:id',
142
+ request: z.object({
143
+ name: z.string(),
144
+ }),
145
+ response: z.object({
146
+ id: z.number(),
147
+ name: z.string(),
148
+ }),
149
+ params: z.object({
150
+ id: z.string(),
151
+ }),
152
+ async handler({ request, params }) {
123
153
  const typedJson = await request.json() // this body will have the correct type
124
154
  return { id: Number(params.id), name: typedJson.name }
125
155
  },
126
- {
127
- body: z.object({
128
- name: z.string(),
129
- }),
130
- response: z.object({
131
- id: z.number(),
132
- name: z.string(),
133
- }),
134
- params: z.object({
135
- id: z.string(),
136
- }),
137
- },
138
- )
156
+ })
139
157
  ```
140
158
 
141
159
  ## Generate RPC Client
@@ -147,26 +165,35 @@ import { z } from 'zod'
147
165
 
148
166
  // Define the app with multiple routes and features
149
167
  const app = new Spiceflow()
150
- .get('/hello/:id', ({ params }) => `Hello, ${params.id}!`)
151
- .post(
152
- '/users',
153
- async ({ request }) => {
168
+ .route({
169
+ method: 'GET',
170
+ path: '/hello/:id',
171
+ handler({ params }) {
172
+ return `Hello, ${params.id}!`
173
+ },
174
+ })
175
+ .route({
176
+ method: 'POST',
177
+ path: '/users',
178
+ async handler({ request }) {
154
179
  const body = await request.json() // here body has type { name?: string, email?: string }
155
180
  return `Created user: ${body.name}`
156
181
  },
157
- {
158
- body: z.object({
159
- name: z.string().optional(),
160
- email: z.string().email().optional(),
161
- }),
182
+ request: z.object({
183
+ name: z.string().optional(),
184
+ email: z.string().email().optional(),
185
+ }),
186
+ })
187
+ .route({
188
+ method: 'GET',
189
+ path: '/stream',
190
+ async *handler() {
191
+ yield 'Start'
192
+ await new Promise((resolve) => setTimeout(resolve, 1000))
193
+ yield 'Middle'
194
+ await new Promise((resolve) => setTimeout(resolve, 1000))
195
+ yield 'End'
162
196
  },
163
- )
164
- .get('/stream', async function* () {
165
- yield 'Start'
166
- await new Promise((resolve) => setTimeout(resolve, 1000))
167
- yield 'Middle'
168
- await new Promise((resolve) => setTimeout(resolve, 1000))
169
- yield 'End'
170
197
  })
171
198
 
172
199
  // Create the client
@@ -214,16 +241,25 @@ import { Spiceflow } from 'spiceflow'
214
241
  import { z } from 'zod'
215
242
 
216
243
  const mainApp = new Spiceflow()
217
- .post(
218
- '/users',
219
- async ({ request }) => `Created user: ${(await request.json()).name}`,
220
- {
221
- body: z.object({
222
- name: z.string(),
223
- }),
244
+ .route({
245
+ method: 'POST',
246
+ path: '/users',
247
+ async handler({ request }) {
248
+ return `Created user: ${(await request.json()).name}`
224
249
  },
250
+ request: z.object({
251
+ name: z.string(),
252
+ }),
253
+ })
254
+ .use(
255
+ new Spiceflow().route({
256
+ method: 'GET',
257
+ path: '/',
258
+ handler() {
259
+ return 'Users list'
260
+ },
261
+ }),
225
262
  )
226
- .use(new Spiceflow().get('/', () => 'Users list'))
227
263
  ```
228
264
 
229
265
  ## Base Path
@@ -232,7 +268,13 @@ const mainApp = new Spiceflow()
232
268
  import { Spiceflow } from 'spiceflow'
233
269
 
234
270
  const app = new Spiceflow({ basePath: '/api/v1' })
235
- app.get('/hello', () => 'Hello') // Accessible at /api/v1/hello
271
+ app.route({
272
+ method: 'GET',
273
+ path: '/hello',
274
+ handler() {
275
+ return 'Hello'
276
+ },
277
+ }) // Accessible at /api/v1/hello
236
278
  ```
237
279
 
238
280
  ## Async Generators (Streaming)
@@ -242,12 +284,16 @@ Async generators will create a server sent event response.
242
284
  ```ts
243
285
  import { Spiceflow } from 'spiceflow'
244
286
 
245
- const app = new Spiceflow().get('/sseStream', async function* () {
246
- yield { message: 'Start' }
247
- await new Promise((resolve) => setTimeout(resolve, 1000))
248
- yield { message: 'Middle' }
249
- await new Promise((resolve) => setTimeout(resolve, 1000))
250
- yield { message: 'End' }
287
+ const app = new Spiceflow().route({
288
+ method: 'GET',
289
+ path: '/sseStream',
290
+ async *handler() {
291
+ yield { message: 'Start' }
292
+ await new Promise((resolve) => setTimeout(resolve, 1000))
293
+ yield { message: 'Middle' }
294
+ await new Promise((resolve) => setTimeout(resolve, 1000))
295
+ yield { message: 'End' }
296
+ },
251
297
  })
252
298
 
253
299
  // Server-Sent Events (SSE) format
@@ -317,15 +363,27 @@ import { Spiceflow } from 'spiceflow'
317
363
  import { createSpiceflowClient } from 'spiceflow/client'
318
364
 
319
365
  const app = new Spiceflow()
320
- .get('/error', () => {
321
- throw new Error('Something went wrong')
366
+ .route({
367
+ method: 'GET',
368
+ path: '/error',
369
+ handler() {
370
+ throw new Error('Something went wrong')
371
+ },
322
372
  })
323
- .get('/unauthorized', () => {
324
- return new Response('Unauthorized access', { status: 401 })
373
+ .route({
374
+ method: 'GET',
375
+ path: '/unauthorized',
376
+ handler() {
377
+ return new Response('Unauthorized access', { status: 401 })
378
+ },
325
379
  })
326
- .get('/success', () => {
327
- throw new Response('Success message', { status: 200 })
328
- return ''
380
+ .route({
381
+ method: 'GET',
382
+ path: '/success',
383
+ handler() {
384
+ throw new Response('Success message', { status: 200 })
385
+ return ''
386
+ },
329
387
  })
330
388
 
331
389
  const client = createSpiceflowClient<typeof app>('http://localhost:3000')
@@ -368,11 +426,23 @@ import { writeFile } from 'node:fs/promises'
368
426
 
369
427
  const app = new Spiceflow()
370
428
  .use(openapi({ path: '/openapi' }))
371
- .get('/users', () => [
372
- { id: 1, name: 'John' },
373
- { id: 2, name: 'Jane' },
374
- ])
375
- .post('/users', ({ request }) => request.json())
429
+ .route({
430
+ method: 'GET',
431
+ path: '/users',
432
+ handler() {
433
+ return [
434
+ { id: 1, name: 'John' },
435
+ { id: 2, name: 'Jane' },
436
+ ]
437
+ },
438
+ })
439
+ .route({
440
+ method: 'POST',
441
+ path: '/users',
442
+ handler({ request }) {
443
+ return request.json()
444
+ },
445
+ })
376
446
 
377
447
  // Create client by passing app instance directly
378
448
  const client = createSpiceflowClient(app)
@@ -401,8 +471,12 @@ new Spiceflow()
401
471
  }
402
472
  return response
403
473
  })
404
- .get('/example', () => {
405
- return { message: 'Hello, World!' }
474
+ .route({
475
+ method: 'GET',
476
+ path: '/example',
477
+ handler() {
478
+ return { message: 'Hello, World!' }
479
+ },
406
480
  })
407
481
  ```
408
482
 
@@ -415,25 +489,29 @@ import { z } from 'zod'
415
489
 
416
490
  const app = new Spiceflow()
417
491
  .use(openapi({ path: '/openapi.json' }))
418
- .get('/hello', () => 'Hello, World!', {
492
+ .route({
493
+ method: 'GET',
494
+ path: '/hello',
495
+ handler() {
496
+ return 'Hello, World!'
497
+ },
419
498
  query: z.object({
420
499
  name: z.string(),
421
500
  age: z.number(),
422
501
  }),
423
502
  response: z.string(),
424
503
  })
425
- .post(
426
- '/user',
427
- () => {
504
+ .route({
505
+ method: 'POST',
506
+ path: '/user',
507
+ handler() {
428
508
  return new Response('Hello, World!')
429
509
  },
430
- {
431
- body: z.object({
432
- name: z.string(),
433
- email: z.string().email(),
434
- }),
435
- },
436
- )
510
+ request: z.object({
511
+ name: z.string(),
512
+ email: z.string().email(),
513
+ }),
514
+ })
437
515
 
438
516
  const openapiSchema = await (
439
517
  await app.handle(new Request('http://localhost:3000/openapi.json'))
@@ -446,7 +524,13 @@ const openapiSchema = await (
446
524
  import { cors } from 'spiceflow/cors'
447
525
  import { Spiceflow } from 'spiceflow'
448
526
 
449
- const app = new Spiceflow().use(cors()).get('/hello', () => 'Hello, World!')
527
+ const app = new Spiceflow().use(cors()).route({
528
+ method: 'GET',
529
+ path: '/hello',
530
+ handler() {
531
+ return 'Hello, World!'
532
+ },
533
+ })
450
534
  ```
451
535
 
452
536
  ## Proxy requests
@@ -527,12 +611,16 @@ new Spiceflow()
527
611
 
528
612
  return response
529
613
  })
530
- .post('/protected', async ({ state }) => {
531
- const { session } = state
532
- if (!session) {
533
- throw new Error('Not logged in')
534
- }
535
- return { ok: true }
614
+ .route({
615
+ method: 'POST',
616
+ path: '/protected',
617
+ async handler({ state }) {
618
+ const { session } = state
619
+ if (!session) {
620
+ throw new Error('Not logged in')
621
+ }
622
+ return { ok: true }
623
+ },
536
624
  })
537
625
  ```
538
626
 
@@ -569,12 +657,22 @@ new Spiceflow()
569
657
 
570
658
  resolveUser()
571
659
  })
572
- .get('/protected', async ({ state }) => {
573
- const userId = await state.userId
574
- if (!userId) throw new Error('Not authenticated')
575
- return { message: 'Protected data' }
660
+ .route({
661
+ method: 'GET',
662
+ path: '/protected',
663
+ async handler({ state }) {
664
+ const userId = await state.userId
665
+ if (!userId) throw new Error('Not authenticated')
666
+ return { message: 'Protected data' }
667
+ },
668
+ })
669
+ .route({
670
+ method: 'GET',
671
+ path: '/public',
672
+ handler() {
673
+ return { message: 'Public data' }
674
+ },
576
675
  })
577
- .get('/public', () => ({ message: 'Public data' }))
578
676
 
579
677
  async function getUser(sessionKey: string) {
580
678
  await new Promise((resolve) => setTimeout(resolve, 100))
@@ -614,11 +712,27 @@ const app = new Spiceflow()
614
712
  // Mount the MCP plugin at /mcp (default path)
615
713
  .use(mcp())
616
714
  // These routes will be available as tools
617
- .get('/hello', () => 'Hello World')
618
- .get('/users/:id', ({ params }) => ({ id: params.id }))
619
- .post('/echo', async ({ request }) => {
620
- const body = await request.json()
621
- return body
715
+ .route({
716
+ method: 'GET',
717
+ path: '/hello',
718
+ handler() {
719
+ return 'Hello World'
720
+ },
721
+ })
722
+ .route({
723
+ method: 'GET',
724
+ path: '/users/:id',
725
+ handler({ params }) {
726
+ return { id: params.id }
727
+ },
728
+ })
729
+ .route({
730
+ method: 'POST',
731
+ path: '/echo',
732
+ async handler({ request }) {
733
+ const body = await request.json()
734
+ return body
735
+ },
622
736
  })
623
737
 
624
738
  // Start the server
@@ -675,9 +789,13 @@ import { Spiceflow } from 'spiceflow'
675
789
  import { openapi } from 'spiceflow/openapi'
676
790
  import { createSpiceflowClient } from 'spiceflow/client'
677
791
 
678
- const app = new Spiceflow()
679
- .use(openapi({ path: '/openapi' }))
680
- .get('/hello', () => 'Hello World')
792
+ const app = new Spiceflow().use(openapi({ path: '/openapi' })).route({
793
+ method: 'GET',
794
+ path: '/hello',
795
+ handler() {
796
+ return 'Hello World'
797
+ },
798
+ })
681
799
 
682
800
  async function main() {
683
801
  console.log('Creating Spiceflow client...')
@@ -728,14 +846,22 @@ interface Env {
728
846
 
729
847
  const app = new Spiceflow()
730
848
  .state('env', null as Env | null)
731
- .get('/kv/:key', async ({ params, state }) => {
732
- const value = await state.env!.KV.get(params.key)
733
- return { key: params.key, value }
849
+ .route({
850
+ method: 'GET',
851
+ path: '/kv/:key',
852
+ async handler({ params, state }) {
853
+ const value = await state.env!.KV.get(params.key)
854
+ return { key: params.key, value }
855
+ },
734
856
  })
735
- .post('/queue', async ({ request, state }) => {
736
- const body = await request.json()
737
- await state.env!.QUEUE.send(body)
738
- return { success: true, message: 'Added to queue' }
857
+ .route({
858
+ method: 'POST',
859
+ path: '/queue',
860
+ async handler({ request, state }) {
861
+ const body = await request.json()
862
+ await state.env!.QUEUE.send(body)
863
+ return { success: true, message: 'Added to queue' }
864
+ },
739
865
  })
740
866
 
741
867
  export default {
@@ -1,7 +1,7 @@
1
1
  import { type Server, type IncomingMessage, type ServerResponse } from 'node:http';
2
- import { type Spiceflow } from './spiceflow.ts';
3
- export declare function listenForNode(app: Spiceflow<any, any, any, any, any, any>, port: number, hostname?: string): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>;
4
- export declare function handleForNode(app: Spiceflow<any, any, any, any, any, any>, req: IncomingMessage, res: ServerResponse, context?: {
2
+ import { AnySpiceflow } from './spiceflow.ts';
3
+ export declare function listenForNode(app: AnySpiceflow, port: number, hostname?: string): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>;
4
+ export declare function handleForNode(app: AnySpiceflow, req: IncomingMessage, res: ServerResponse, context?: {
5
5
  state?: {} | undefined;
6
6
  }): Promise<void>;
7
7
  //# sourceMappingURL=_node-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_node-server.d.ts","sourceRoot":"","sources":["../src/_node-server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,KAAK,cAAc,EAEpB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,gBAAgB,CAAA;AAGjE,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC5C,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,OAAO,eAAe,EAAE,OAAO,cAAc,CAAC,CAAC,CAkBhE;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,EAAE,GAAG,SAAS,CAAA;CAAO,GACvC,OAAO,CAAC,IAAI,CAAC,CA2Ef"}
1
+ {"version":3,"file":"_node-server.d.ts","sourceRoot":"","sources":["../src/_node-server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,KAAK,cAAc,EAEpB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,YAAY,EAAoC,MAAM,gBAAgB,CAAA;AAG/E,wBAAsB,aAAa,CACjC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,OAAO,eAAe,EAAE,OAAO,cAAc,CAAC,CAAC,CAkBhE;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,EAAE,GAAG,SAAS,CAAA;CAAO,GACvC,OAAO,CAAC,IAAI,CAAC,CA2Ef"}
@@ -1,4 +1,4 @@
1
- import type { Spiceflow } from '../spiceflow.ts';
1
+ import type { AnySpiceflow, Spiceflow } from '../spiceflow.ts';
2
2
  import type { SpiceflowClient } from './types.ts';
3
3
  export { SpiceflowClient };
4
4
  interface SSEEvent {
@@ -10,5 +10,7 @@ export declare class TextDecoderStream extends TransformStream<Uint8Array, strin
10
10
  constructor();
11
11
  }
12
12
  export declare function streamSSEResponse(response: Response): AsyncGenerator<SSEEvent>;
13
- export declare const createSpiceflowClient: <const App extends Spiceflow<any, any, any, any, any, any>>(domain: string | App, config?: SpiceflowClient.Config) => SpiceflowClient.Create<App>;
13
+ export declare const createSpiceflowClient: <const App extends AnySpiceflow>(domain: App | string, config?: SpiceflowClient.Config & (App extends Spiceflow<any, any, infer Singleton, any, any, any, any> ? {
14
+ state?: Singleton["state"];
15
+ } : {})) => SpiceflowClient.Create<App>;
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,CAAA;AA4G1B,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,GAAG,CAAA;IACT,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,qBAAa,iBAAkB,SAAQ,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC;;CAwBzE;AAED,wBAAuB,iBAAiB,CACtC,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,QAAQ,CAAC,CAmB1B;AAqSD,eAAO,MAAM,qBAAqB,GAChC,KAAK,CAAC,GAAG,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAEzD,QAAQ,MAAM,GAAG,GAAG,EACpB,SAAQ,eAAe,CAAC,MAAW,KAClC,eAAe,CAAC,MAAM,CAAC,GAAG,CAa5B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAI9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,CAAA;AA4G1B,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,GAAG,CAAA;IACT,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,qBAAa,iBAAkB,SAAQ,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC;;CAwBzE;AAED,wBAAuB,iBAAiB,CACtC,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,QAAQ,CAAC,CAmB1B;AA0SD,eAAO,MAAM,qBAAqB,GAChC,KAAK,CAAC,GAAG,SAAS,YAAY,EAE9B,QAAQ,GAAG,GAAG,MAAM,EACpB,SAAS,eAAe,CAAC,MAAM,GAC7B,CAAC,GAAG,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GACjE;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;CAAE,GAC9B,EAAE,CAAC,KACR,eAAe,CAAC,MAAM,CAAC,GAAG,CAc5B,CAAA"}