spiceflow 1.10.1 → 1.11.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
@@ -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
@@ -56,11 +66,22 @@ import { Spiceflow } from 'spiceflow'
56
66
  // DO NOT declare the app separately and add routes later
57
67
  const app = new Spiceflow()
58
68
 
69
+ // Do NOT do this! Defining routes separately will lose type safety
70
+ app.route({
71
+ method: 'GET',
72
+ path: '/hello',
73
+ handler() {
74
+ return 'Hello, World!'
75
+ },
76
+ })
59
77
  // 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
78
+ app.route({
79
+ method: 'POST',
80
+ path: '/echo',
81
+ async handler({ request }) {
82
+ const body = await request.json()
83
+ return body
84
+ },
64
85
  })
65
86
  ```
66
87
 
@@ -93,19 +114,18 @@ This project shares many inspirations with Hono with many differences
93
114
  import { z } from 'zod'
94
115
  import { Spiceflow } from 'spiceflow'
95
116
 
96
- new Spiceflow().post(
97
- '/users',
98
- async ({ request }) => {
117
+ new Spiceflow().route({
118
+ method: 'POST',
119
+ path: '/users',
120
+ request: z.object({
121
+ name: z.string(),
122
+ email: z.string().email(),
123
+ }),
124
+ async handler({ request }) {
99
125
  const body = await request.json() // here body has type { name: string, email: string }
100
126
  return `Created user: ${body.name}`
101
127
  },
102
- {
103
- body: z.object({
104
- name: z.string(),
105
- email: z.string().email(),
106
- }),
107
- },
108
- )
128
+ })
109
129
  ```
110
130
 
111
131
  > Notice that to get the body of the request, you need to call `request.json()` to parse the body as JSON.
@@ -117,25 +137,24 @@ new Spiceflow().post(
117
137
  import { z } from 'zod'
118
138
  import { Spiceflow } from 'spiceflow'
119
139
 
120
- new Spiceflow().get(
121
- '/users/:id',
122
- ({ request, params }) => {
140
+ new Spiceflow().route({
141
+ method: 'GET',
142
+ path: '/users/:id',
143
+ request: z.object({
144
+ name: z.string(),
145
+ }),
146
+ response: z.object({
147
+ id: z.number(),
148
+ name: z.string(),
149
+ }),
150
+ params: z.object({
151
+ id: z.string(),
152
+ }),
153
+ async handler({ request, params }) {
123
154
  const typedJson = await request.json() // this body will have the correct type
124
155
  return { id: Number(params.id), name: typedJson.name }
125
156
  },
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
- )
157
+ })
139
158
  ```
140
159
 
141
160
  ## Generate RPC Client
@@ -147,26 +166,35 @@ import { z } from 'zod'
147
166
 
148
167
  // Define the app with multiple routes and features
149
168
  const app = new Spiceflow()
150
- .get('/hello/:id', ({ params }) => `Hello, ${params.id}!`)
151
- .post(
152
- '/users',
153
- async ({ request }) => {
169
+ .route({
170
+ method: 'GET',
171
+ path: '/hello/:id',
172
+ handler({ params }) {
173
+ return `Hello, ${params.id}!`
174
+ },
175
+ })
176
+ .route({
177
+ method: 'POST',
178
+ path: '/users',
179
+ async handler({ request }) {
154
180
  const body = await request.json() // here body has type { name?: string, email?: string }
155
181
  return `Created user: ${body.name}`
156
182
  },
157
- {
158
- body: z.object({
159
- name: z.string().optional(),
160
- email: z.string().email().optional(),
161
- }),
183
+ request: z.object({
184
+ name: z.string().optional(),
185
+ email: z.string().email().optional(),
186
+ }),
187
+ })
188
+ .route({
189
+ method: 'GET',
190
+ path: '/stream',
191
+ async *handler() {
192
+ yield 'Start'
193
+ await new Promise((resolve) => setTimeout(resolve, 1000))
194
+ yield 'Middle'
195
+ await new Promise((resolve) => setTimeout(resolve, 1000))
196
+ yield 'End'
162
197
  },
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
198
  })
171
199
 
172
200
  // Create the client
@@ -214,16 +242,25 @@ import { Spiceflow } from 'spiceflow'
214
242
  import { z } from 'zod'
215
243
 
216
244
  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
- }),
245
+ .route({
246
+ method: 'POST',
247
+ path: '/users',
248
+ async handler({ request }) {
249
+ return `Created user: ${(await request.json()).name}`
224
250
  },
251
+ request: z.object({
252
+ name: z.string(),
253
+ }),
254
+ })
255
+ .use(
256
+ new Spiceflow().route({
257
+ method: 'GET',
258
+ path: '/',
259
+ handler() {
260
+ return 'Users list'
261
+ },
262
+ }),
225
263
  )
226
- .use(new Spiceflow().get('/', () => 'Users list'))
227
264
  ```
228
265
 
229
266
  ## Base Path
@@ -232,7 +269,13 @@ const mainApp = new Spiceflow()
232
269
  import { Spiceflow } from 'spiceflow'
233
270
 
234
271
  const app = new Spiceflow({ basePath: '/api/v1' })
235
- app.get('/hello', () => 'Hello') // Accessible at /api/v1/hello
272
+ app.route({
273
+ method: 'GET',
274
+ path: '/hello',
275
+ handler() {
276
+ return 'Hello'
277
+ },
278
+ }) // Accessible at /api/v1/hello
236
279
  ```
237
280
 
238
281
  ## Async Generators (Streaming)
@@ -242,12 +285,16 @@ Async generators will create a server sent event response.
242
285
  ```ts
243
286
  import { Spiceflow } from 'spiceflow'
244
287
 
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' }
288
+ const app = new Spiceflow().route({
289
+ method: 'GET',
290
+ path: '/sseStream',
291
+ async *handler() {
292
+ yield { message: 'Start' }
293
+ await new Promise((resolve) => setTimeout(resolve, 1000))
294
+ yield { message: 'Middle' }
295
+ await new Promise((resolve) => setTimeout(resolve, 1000))
296
+ yield { message: 'End' }
297
+ },
251
298
  })
252
299
 
253
300
  // Server-Sent Events (SSE) format
@@ -317,15 +364,27 @@ import { Spiceflow } from 'spiceflow'
317
364
  import { createSpiceflowClient } from 'spiceflow/client'
318
365
 
319
366
  const app = new Spiceflow()
320
- .get('/error', () => {
321
- throw new Error('Something went wrong')
367
+ .route({
368
+ method: 'GET',
369
+ path: '/error',
370
+ handler() {
371
+ throw new Error('Something went wrong')
372
+ },
322
373
  })
323
- .get('/unauthorized', () => {
324
- return new Response('Unauthorized access', { status: 401 })
374
+ .route({
375
+ method: 'GET',
376
+ path: '/unauthorized',
377
+ handler() {
378
+ return new Response('Unauthorized access', { status: 401 })
379
+ },
325
380
  })
326
- .get('/success', () => {
327
- throw new Response('Success message', { status: 200 })
328
- return ''
381
+ .route({
382
+ method: 'GET',
383
+ path: '/success',
384
+ handler() {
385
+ throw new Response('Success message', { status: 200 })
386
+ return ''
387
+ },
329
388
  })
330
389
 
331
390
  const client = createSpiceflowClient<typeof app>('http://localhost:3000')
@@ -368,11 +427,23 @@ import { writeFile } from 'node:fs/promises'
368
427
 
369
428
  const app = new Spiceflow()
370
429
  .use(openapi({ path: '/openapi' }))
371
- .get('/users', () => [
372
- { id: 1, name: 'John' },
373
- { id: 2, name: 'Jane' },
374
- ])
375
- .post('/users', ({ request }) => request.json())
430
+ .route({
431
+ method: 'GET',
432
+ path: '/users',
433
+ handler() {
434
+ return [
435
+ { id: 1, name: 'John' },
436
+ { id: 2, name: 'Jane' },
437
+ ]
438
+ },
439
+ })
440
+ .route({
441
+ method: 'POST',
442
+ path: '/users',
443
+ handler({ request }) {
444
+ return request.json()
445
+ },
446
+ })
376
447
 
377
448
  // Create client by passing app instance directly
378
449
  const client = createSpiceflowClient(app)
@@ -401,8 +472,12 @@ new Spiceflow()
401
472
  }
402
473
  return response
403
474
  })
404
- .get('/example', () => {
405
- return { message: 'Hello, World!' }
475
+ .route({
476
+ method: 'GET',
477
+ path: '/example',
478
+ handler() {
479
+ return { message: 'Hello, World!' }
480
+ },
406
481
  })
407
482
  ```
408
483
 
@@ -415,25 +490,29 @@ import { z } from 'zod'
415
490
 
416
491
  const app = new Spiceflow()
417
492
  .use(openapi({ path: '/openapi.json' }))
418
- .get('/hello', () => 'Hello, World!', {
493
+ .route({
494
+ method: 'GET',
495
+ path: '/hello',
496
+ handler() {
497
+ return 'Hello, World!'
498
+ },
419
499
  query: z.object({
420
500
  name: z.string(),
421
501
  age: z.number(),
422
502
  }),
423
503
  response: z.string(),
424
504
  })
425
- .post(
426
- '/user',
427
- () => {
505
+ .route({
506
+ method: 'POST',
507
+ path: '/user',
508
+ handler() {
428
509
  return new Response('Hello, World!')
429
510
  },
430
- {
431
- body: z.object({
432
- name: z.string(),
433
- email: z.string().email(),
434
- }),
435
- },
436
- )
511
+ request: z.object({
512
+ name: z.string(),
513
+ email: z.string().email(),
514
+ }),
515
+ })
437
516
 
438
517
  const openapiSchema = await (
439
518
  await app.handle(new Request('http://localhost:3000/openapi.json'))
@@ -446,7 +525,13 @@ const openapiSchema = await (
446
525
  import { cors } from 'spiceflow/cors'
447
526
  import { Spiceflow } from 'spiceflow'
448
527
 
449
- const app = new Spiceflow().use(cors()).get('/hello', () => 'Hello, World!')
528
+ const app = new Spiceflow().use(cors()).route({
529
+ method: 'GET',
530
+ path: '/hello',
531
+ handler() {
532
+ return 'Hello, World!'
533
+ },
534
+ })
450
535
  ```
451
536
 
452
537
  ## Proxy requests
@@ -527,12 +612,16 @@ new Spiceflow()
527
612
 
528
613
  return response
529
614
  })
530
- .post('/protected', async ({ state }) => {
531
- const { session } = state
532
- if (!session) {
533
- throw new Error('Not logged in')
534
- }
535
- return { ok: true }
615
+ .route({
616
+ method: 'POST',
617
+ path: '/protected',
618
+ async handler({ state }) {
619
+ const { session } = state
620
+ if (!session) {
621
+ throw new Error('Not logged in')
622
+ }
623
+ return { ok: true }
624
+ },
536
625
  })
537
626
  ```
538
627
 
@@ -569,12 +658,22 @@ new Spiceflow()
569
658
 
570
659
  resolveUser()
571
660
  })
572
- .get('/protected', async ({ state }) => {
573
- const userId = await state.userId
574
- if (!userId) throw new Error('Not authenticated')
575
- return { message: 'Protected data' }
661
+ .route({
662
+ method: 'GET',
663
+ path: '/protected',
664
+ async handler({ state }) {
665
+ const userId = await state.userId
666
+ if (!userId) throw new Error('Not authenticated')
667
+ return { message: 'Protected data' }
668
+ },
669
+ })
670
+ .route({
671
+ method: 'GET',
672
+ path: '/public',
673
+ handler() {
674
+ return { message: 'Public data' }
675
+ },
576
676
  })
577
- .get('/public', () => ({ message: 'Public data' }))
578
677
 
579
678
  async function getUser(sessionKey: string) {
580
679
  await new Promise((resolve) => setTimeout(resolve, 100))
@@ -614,11 +713,27 @@ const app = new Spiceflow()
614
713
  // Mount the MCP plugin at /mcp (default path)
615
714
  .use(mcp())
616
715
  // 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
716
+ .route({
717
+ method: 'GET',
718
+ path: '/hello',
719
+ handler() {
720
+ return 'Hello World'
721
+ },
722
+ })
723
+ .route({
724
+ method: 'GET',
725
+ path: '/users/:id',
726
+ handler({ params }) {
727
+ return { id: params.id }
728
+ },
729
+ })
730
+ .route({
731
+ method: 'POST',
732
+ path: '/echo',
733
+ async handler({ request }) {
734
+ const body = await request.json()
735
+ return body
736
+ },
622
737
  })
623
738
 
624
739
  // Start the server
@@ -675,9 +790,13 @@ import { Spiceflow } from 'spiceflow'
675
790
  import { openapi } from 'spiceflow/openapi'
676
791
  import { createSpiceflowClient } from 'spiceflow/client'
677
792
 
678
- const app = new Spiceflow()
679
- .use(openapi({ path: '/openapi' }))
680
- .get('/hello', () => 'Hello World')
793
+ const app = new Spiceflow().use(openapi({ path: '/openapi' })).route({
794
+ method: 'GET',
795
+ path: '/hello',
796
+ handler() {
797
+ return 'Hello World'
798
+ },
799
+ })
681
800
 
682
801
  async function main() {
683
802
  console.log('Creating Spiceflow client...')
@@ -728,14 +847,22 @@ interface Env {
728
847
 
729
848
  const app = new Spiceflow()
730
849
  .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 }
850
+ .route({
851
+ method: 'GET',
852
+ path: '/kv/:key',
853
+ async handler({ params, state }) {
854
+ const value = await state.env!.KV.get(params.key)
855
+ return { key: params.key, value }
856
+ },
734
857
  })
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' }
858
+ .route({
859
+ method: 'POST',
860
+ path: '/queue',
861
+ async handler({ request, state }) {
862
+ const body = await request.json()
863
+ await state.env!.QUEUE.send(body)
864
+ return { success: true, message: 'Added to queue' }
865
+ },
739
866
  })
740
867
 
741
868
  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"}