arri 0.44.0 → 0.45.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.
@@ -1,1250 +0,0 @@
1
- import { isRpcHttpMethod, removeDisallowedChars, SCHEMA_VERSION } from 'arri-codegen-utils';
2
- import { isError, setResponseStatus, setResponseHeader, send, eventHandler, isPreflightRequest, readRawBody, getValidatedQuery, defineEventHandler, getQuery, setHeader, defineWebSocketHandler, createRouter, createApp } from 'h3';
3
- import { a, isAObjectSchema, isADiscriminatorSchema } from 'arri-validate';
4
- import { createEventStream } from 'h3-sse';
5
- import { kebabCase, pascalCase } from 'scule';
6
-
7
- var __defProp$4 = Object.defineProperty;
8
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __publicField$4 = (obj, key, value) => {
10
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
11
- return value;
12
- };
13
- class ArriServerError extends Error {
14
- constructor(err) {
15
- super(err.message);
16
- __publicField$4(this, "code");
17
- __publicField$4(this, "data");
18
- this.code = err.code;
19
- this.data = err.data;
20
- if (err.stack) {
21
- this.stack = err.stack;
22
- }
23
- }
24
- }
25
- const ArriServerErrorResponse = a.object({
26
- code: a.int16(),
27
- message: a.string(),
28
- stack: a.optional(a.array(a.string())),
29
- data: a.optional(a.any())
30
- });
31
- function defineError(statusCode, input = {}) {
32
- const defaultVals = errorResponseDefaults[statusCode];
33
- return new ArriServerError({
34
- code: statusCode,
35
- message: input.message ?? defaultVals?.message ?? "An unknown error occurred",
36
- data: input.data
37
- });
38
- }
39
- function errorResponseFromValidationErrors(errors, prefixText = `Missing or invalid parameters`) {
40
- const errorParts = [];
41
- for (const err of errors) {
42
- const propName = err.instancePath?.split("/") ?? [];
43
- if (propName.length > 0) {
44
- propName.shift();
45
- }
46
- if (!errorParts.includes(propName.join("."))) {
47
- errorParts.push(propName.join("."));
48
- }
49
- }
50
- throw defineError(400, {
51
- message: `${prefixText}: [${errorParts.join(",")}]`,
52
- data: errors
53
- });
54
- }
55
- const errorResponseDefaults = {
56
- 400: {
57
- name: "BAD_REQUEST",
58
- message: "Bad Request"
59
- },
60
- 401: {
61
- name: "UNAUTHORIZED",
62
- message: "Unauthorized"
63
- },
64
- 402: {
65
- name: "PAYMENT_REQUIRED",
66
- message: "Payment Required"
67
- },
68
- 403: {
69
- name: "FORBIDDEN",
70
- message: "Forbidden"
71
- },
72
- 404: {
73
- name: "NOT_FOUND",
74
- message: "Not Found"
75
- },
76
- 405: {
77
- name: "METHOD_NOT_ALLOWED",
78
- message: "Method Not Allowed"
79
- },
80
- 406: {
81
- name: "NOT_ACCEPTABLE",
82
- message: "Not Acceptable"
83
- },
84
- 407: {
85
- name: "PROXY_AUTHENTICATION_REQUIRED",
86
- message: "Proxy Authentication Required"
87
- },
88
- 408: {
89
- name: "REQUEST_TIMEOUT",
90
- message: "Request Timeout"
91
- },
92
- 409: {
93
- name: "CONFLICT",
94
- message: "Conflict"
95
- },
96
- 410: {
97
- name: "GONE",
98
- message: "Gone"
99
- },
100
- 411: {
101
- name: "LENGTH_REQUIRED",
102
- message: "Length Required"
103
- },
104
- 412: {
105
- name: "PRECONDITION_FAILED",
106
- message: "Precondition Failed"
107
- },
108
- 413: {
109
- name: "REQUEST_TOO_LONG",
110
- message: "Request Entity Too Large"
111
- },
112
- 414: {
113
- name: "REQUEST_URI_TOO_LONG",
114
- message: "Request-URI Too Long"
115
- },
116
- 415: {
117
- name: "UNSUPPORTED_MEDIA_TYPE",
118
- message: "Unsupported Media Type"
119
- },
120
- 416: {
121
- name: "REQUESTED_RANGE_NOT_SATISFIABLE",
122
- message: "Requested Range Not Satisfiable"
123
- },
124
- 417: {
125
- name: "EXPECTATION_FAILED",
126
- message: "Expectation Failed"
127
- },
128
- 418: {
129
- name: "IM_A_TEAPOT",
130
- message: "I'm a teapot"
131
- },
132
- 419: {
133
- name: "INSUFFICIENT_SPACE_ON_RESOURCE",
134
- message: "Insufficient Space on Resource"
135
- },
136
- 420: {
137
- name: "METHOD_FAILURE",
138
- message: "Method Failure"
139
- },
140
- 421: {
141
- name: "MISDIRECTED_REQUEST",
142
- message: "Misdirected Request"
143
- },
144
- 422: {
145
- name: "UNPROCESSABLE_ENTITY",
146
- message: "Unprocessable Entity"
147
- },
148
- 423: {
149
- name: "LOCKED",
150
- message: "Locked"
151
- },
152
- 424: {
153
- name: "FAILED_DEPENDENCY",
154
- message: "Failed Dependency"
155
- },
156
- 428: {
157
- name: "PRECONDITION_REQUIRED",
158
- message: "Precondition Required"
159
- },
160
- 429: {
161
- name: "TOO_MANY_REQUESTS",
162
- message: "Too Many Requests"
163
- },
164
- 431: {
165
- name: "REQUEST_HEADER_FIELDS_TOO_LARGE",
166
- message: "Request Header Fields Too Large"
167
- },
168
- 451: {
169
- name: "UNAVAILABLE_FOR_LEGAL_REASONS",
170
- message: "Unavailable For Legal Reasons"
171
- },
172
- 500: {
173
- name: "INTERNAL_SERVER_ERROR",
174
- message: "Internal Server Error"
175
- },
176
- 501: {
177
- name: "NOT_IMPLEMENTED",
178
- message: "Not Implemented"
179
- },
180
- 502: {
181
- name: "BAD_GATEWAY",
182
- message: "Bad Gateway"
183
- },
184
- 503: {
185
- name: "SERVICE_UNAVAILABLE",
186
- message: "Service Unavailable"
187
- },
188
- 504: {
189
- name: "GATEWAY_TIMEOUT",
190
- message: "Gateway Timeout"
191
- },
192
- 505: {
193
- name: "HTTP_VERSION_NOT_SUPPORTED",
194
- message: "HTTP Version Not Supported"
195
- },
196
- 507: {
197
- name: "INSUFFICIENT_STORAGE",
198
- message: "Insufficient Storage"
199
- },
200
- 511: {
201
- name: "NETWORK_AUTHENTICATION_REQUIRED",
202
- message: "Network Authentication Required"
203
- }
204
- };
205
- async function handleH3Error(err, event, onError, debug) {
206
- let arriErr;
207
- if (err instanceof ArriServerError) {
208
- arriErr = err;
209
- } else if (isError(err)) {
210
- arriErr = new ArriServerError({
211
- code: err.statusCode,
212
- message: err.message,
213
- stack: err.stack,
214
- data: err.data
215
- });
216
- } else if (err instanceof Error) {
217
- arriErr = new ArriServerError({
218
- code: 500,
219
- message: err.message,
220
- data: err,
221
- stack: err.stack
222
- });
223
- } else {
224
- arriErr = new ArriServerError({
225
- code: 500,
226
- message: `An unknown error occurred`,
227
- data: err
228
- });
229
- }
230
- if (!arriErr) {
231
- return;
232
- }
233
- setResponseStatus(event, arriErr.code);
234
- if (onError) {
235
- await onError(arriErr, event);
236
- }
237
- if (event.handled) {
238
- return;
239
- }
240
- return sendArriError(event, arriErr, debug);
241
- }
242
- async function sendArriError(event, error, debug) {
243
- const payload = {
244
- code: error.code,
245
- message: error.message,
246
- data: error.data
247
- };
248
- if (debug) {
249
- payload.stack = error.stack?.split("\n").map((l) => l.trim());
250
- }
251
- setResponseHeader(event, "Content-Type", "application/json");
252
- setResponseStatus(event, error.code);
253
- return send(event, JSON.stringify(payload));
254
- }
255
-
256
- function isRpcParamSchema(input) {
257
- return isAObjectSchema(input) || isADiscriminatorSchema(input);
258
- }
259
- function isRpc(input) {
260
- return typeof input === "object" && input !== null && "method" in input && isRpcHttpMethod(input.method) && "handler" in input && typeof input.handler === "function";
261
- }
262
- function defineRpc(config) {
263
- config.transport = "http";
264
- return config;
265
- }
266
- function createHttpRpcDefinition(rpcName, httpPath, procedure) {
267
- let method;
268
- if (procedure.isEventStream === true) {
269
- method = procedure.method ?? "get";
270
- } else {
271
- method = procedure.method ?? "post";
272
- }
273
- return {
274
- transport: "http",
275
- description: procedure.description,
276
- path: httpPath,
277
- method,
278
- params: getRpcParamName(rpcName, procedure),
279
- response: getRpcResponseDefinition(rpcName, procedure),
280
- isDeprecated: procedure.isDeprecated,
281
- isEventStream: procedure.isEventStream === true ? true : void 0
282
- };
283
- }
284
- function getRpcPath(rpcName, prefix = "") {
285
- const path = rpcName.split(".").map(
286
- (part) => removeDisallowedChars(
287
- kebabCase(part),
288
- `!@#$%^&*()+=[]{}|\\;:'"<>,./?`
289
- )
290
- ).join("/");
291
- const finalPath = prefix ? `/${prefix}/${path}` : `/${path}`;
292
- return finalPath;
293
- }
294
- function getRpcParamName(rpcName, procedure) {
295
- if (!isRpcParamSchema(procedure.params)) {
296
- return void 0;
297
- }
298
- const nameParts = rpcName.split(".").map(
299
- (part) => removeDisallowedChars(part, `!@#$%^&*()+=[]{}|\\;:'"<>,./?`)
300
- );
301
- const paramName = procedure.params.metadata.id ?? pascalCase(`${nameParts.join(`_`)}_params`);
302
- return paramName;
303
- }
304
- function getRpcResponseName(rpcName, procedure) {
305
- if (!isRpcParamSchema(procedure.response)) {
306
- return void 0;
307
- }
308
- const nameParts = rpcName.split(".").map(
309
- (part) => removeDisallowedChars(part, `!@#$%^&*()+=[]{}|\\;:'"<>,./?`)
310
- );
311
- const responseName = procedure.response.metadata.id ?? pascalCase(`${nameParts.join("_")}_response`);
312
- return responseName;
313
- }
314
- function getRpcResponseDefinition(rpcName, procedure) {
315
- if (!isRpcParamSchema(procedure.response)) {
316
- return void 0;
317
- }
318
- const name = getRpcResponseName(rpcName, procedure);
319
- if (!name) {
320
- return void 0;
321
- }
322
- return name;
323
- }
324
- function registerRpc(router, path, procedure, opts) {
325
- let responseValidator;
326
- try {
327
- responseValidator = procedure.response ? a.compile(procedure.response) : void 0;
328
- } catch (err) {
329
- console.error("ERROR COMPILING VALIDATOR", err);
330
- }
331
- const httpMethod = procedure.method ?? "post";
332
- const handler = eventHandler(async (event) => {
333
- event.context.rpcName = procedure.name;
334
- if (isPreflightRequest(event)) {
335
- return "ok";
336
- }
337
- try {
338
- if (opts.onRequest) {
339
- await opts.onRequest(event);
340
- }
341
- if (opts.middleware.length) {
342
- for (const m of opts.middleware) {
343
- await m(event);
344
- }
345
- }
346
- if (isRpcParamSchema(procedure.params)) {
347
- await validateRpcRequestInput(
348
- event,
349
- httpMethod,
350
- procedure.params
351
- );
352
- }
353
- const response = await procedure.handler(
354
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
355
- event.context,
356
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
357
- event
358
- );
359
- event.context.response = response;
360
- if (opts.onBeforeResponse) {
361
- await opts.onBeforeResponse(event);
362
- }
363
- if (typeof response === "object") {
364
- if (!responseValidator?.validate(response)) {
365
- const errors = a.errors(procedure.response, response);
366
- throw defineError(500, {
367
- message: "Failed to serialize response. Response does not match specified schema",
368
- data: errors
369
- });
370
- }
371
- setResponseHeader(event, "Content-Type", "application/json");
372
- await send(
373
- event,
374
- responseValidator?.serialize(response) ?? JSON.stringify(response)
375
- );
376
- } else {
377
- setResponseHeader(event, "Content-Type", "application/json");
378
- await send(event, `{}`);
379
- }
380
- if (opts.onAfterResponse) {
381
- await opts.onAfterResponse(event);
382
- }
383
- if (procedure.postHandler) {
384
- await procedure.postHandler(
385
- event.context,
386
- event
387
- );
388
- }
389
- } catch (err) {
390
- await handleH3Error(err, event, opts.onError, opts.debug ?? false);
391
- }
392
- return "";
393
- });
394
- switch (httpMethod) {
395
- case "get":
396
- router.get(path, handler);
397
- break;
398
- case "delete":
399
- router.delete(path, handler);
400
- break;
401
- case "patch":
402
- router.patch(path, handler);
403
- break;
404
- case "put":
405
- router.put(path, handler);
406
- break;
407
- case "post":
408
- default:
409
- router.post(path, handler);
410
- break;
411
- }
412
- }
413
- async function validateRpcRequestInput(event, httpMethod, schema) {
414
- switch (httpMethod) {
415
- case "get": {
416
- const parsedParams = await getValidatedQuery(
417
- event,
418
- (input) => a.safeCoerce(schema, input)
419
- );
420
- if (parsedParams.success) {
421
- event.context.params = parsedParams.value;
422
- } else {
423
- const errParts = [];
424
- for (const err of parsedParams.error.errors) {
425
- const errPath = err.instancePath.split("/");
426
- errPath.shift();
427
- const propName = errPath.join(".");
428
- if (!errParts.includes(propName)) {
429
- errParts.push(propName);
430
- }
431
- }
432
- const message = `Missing or invalid url query parameters: [${errParts.join(
433
- ", "
434
- )}]`;
435
- throw defineError(400, {
436
- message,
437
- data: parsedParams.error
438
- });
439
- }
440
- break;
441
- }
442
- case "delete":
443
- case "patch":
444
- case "post":
445
- case "put": {
446
- const body = await readRawBody(event);
447
- if (!body) {
448
- throw defineError(400, {
449
- message: `Invalid request body. Expected object. Got undefined.`
450
- });
451
- }
452
- const parsedParams = a.safeParse(schema, body);
453
- if (!parsedParams.success) {
454
- const errorParts = [];
455
- for (const err of parsedParams.error.errors) {
456
- const errPath = err.instancePath.split("/");
457
- errPath.shift();
458
- if (!errorParts.includes(errPath.join("."))) {
459
- errorParts.push(errPath.join("."));
460
- }
461
- }
462
- throw defineError(400, {
463
- message: `Invalid request body. Affected properties [${errorParts.join(
464
- ", "
465
- )}]`,
466
- data: parsedParams.error
467
- });
468
- }
469
- event.context.params = parsedParams.value;
470
- break;
471
- }
472
- }
473
- }
474
-
475
- var __defProp$3 = Object.defineProperty;
476
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
477
- var __publicField$3 = (obj, key, value) => {
478
- __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
479
- return value;
480
- };
481
- function defineEventStreamRpc(config) {
482
- return {
483
- ...config,
484
- method: config.method ?? "get",
485
- isEventStream: true,
486
- transport: "http"
487
- };
488
- }
489
- function isEventStreamRpc(input) {
490
- return isRpc(input) && "isEventStream" in input && input.isEventStream === true;
491
- }
492
- class EventStreamConnection {
493
- constructor(event, opts) {
494
- __publicField$3(this, "lastEventId");
495
- __publicField$3(this, "validationErrors");
496
- __publicField$3(this, "validator");
497
- __publicField$3(this, "serializer");
498
- // for some reason Rollup cannot output DTS when this is set to NodeJS.Timeout
499
- __publicField$3(this, "pingInterval");
500
- __publicField$3(this, "pingIntervalMs");
501
- __publicField$3(this, "eventStream");
502
- this.eventStream = createEventStream(event);
503
- this.lastEventId = this.eventStream.lastEventId;
504
- this.pingIntervalMs = opts.pingInterval ?? 6e4;
505
- this.serializer = opts.serializer;
506
- this.validator = opts.validator;
507
- this.validationErrors = opts.validationErrors;
508
- this.eventStream.onClose(() => {
509
- this.cleanup();
510
- });
511
- }
512
- /**
513
- * Send the stream to the client. This must be called before sending any events.
514
- */
515
- send() {
516
- void this.eventStream.send();
517
- this.pingInterval = setInterval(async () => {
518
- await this.eventStream.push({
519
- event: "ping",
520
- data: ""
521
- });
522
- }, this.pingIntervalMs);
523
- }
524
- async push(data, eventId) {
525
- if (Array.isArray(data)) {
526
- const events = [];
527
- for (const item of data) {
528
- if (this.validator(item)) {
529
- events.push({
530
- id: eventId,
531
- event: "message",
532
- data: this.serializer(item)
533
- });
534
- continue;
535
- }
536
- const errors2 = this.validationErrors(item);
537
- const errorResponse2 = defineError(500, {
538
- message: "Failed to serialize response. Response does not match specified schema.",
539
- data: errors2
540
- });
541
- events.push({
542
- id: eventId,
543
- event: "error",
544
- data: JSON.stringify(errorResponse2)
545
- });
546
- }
547
- await this.eventStream.push(events);
548
- return;
549
- }
550
- if (this.validator(data)) {
551
- await this.eventStream.push({
552
- id: eventId,
553
- event: "message",
554
- data: this.serializer(data)
555
- });
556
- return;
557
- }
558
- const errors = this.validationErrors(data);
559
- const errorResponse = defineError(500, {
560
- message: "Failed to serialize response. Response does not match specified schema.",
561
- data: errors
562
- });
563
- await this.eventStream.push({
564
- id: eventId,
565
- event: "error",
566
- data: JSON.stringify(errorResponse)
567
- });
568
- }
569
- // /**
570
- // * Push a custom event. These events will need to be parsed manually using the `onEvent` hooks of any generated clients.
571
- // * Note events with the name "error" or "message" cannot be used for custom events.
572
- // */
573
- // async pushCustomEvent(event: SseEvent): Promise<void> {
574
- // if (event.event === "message") {
575
- // throw new Error(
576
- // `Event type "message" is the default event type. Therefore it cannot be used when pushing custom events.`,
577
- // );
578
- // }
579
- // if (event.event === "error") {
580
- // throw new Error(
581
- // `Event type "error" is reserved for the pushError() method. Therefore it cannot be used when pushing custom events.`,
582
- // );
583
- // }
584
- // }
585
- /**
586
- * Publish an error event. This will trigger the `onError` hooks of any connected clients.
587
- */
588
- async pushError(error, eventId) {
589
- await this.eventStream.push({
590
- id: eventId,
591
- event: "error",
592
- data: JSON.stringify(error)
593
- });
594
- }
595
- cleanup() {
596
- if (this.pingInterval) {
597
- clearInterval(this.pingInterval);
598
- }
599
- }
600
- /**
601
- * Tell clients that the stream has ended and close the connection.
602
- */
603
- async close() {
604
- await this.eventStream.push({
605
- event: "done",
606
- data: "this stream has ended"
607
- }).catch();
608
- await this.eventStream.close();
609
- }
610
- onClose(cb) {
611
- this.eventStream.onClose(cb);
612
- }
613
- }
614
- function registerEventStreamRpc(router, path, procedure, opts) {
615
- let responseValidator;
616
- try {
617
- responseValidator = procedure.response ? a.compile(procedure.response) : void 0;
618
- } catch (err) {
619
- console.error("ERROR COMPILING VALIDATOR", err);
620
- }
621
- const httpMethod = procedure.method ?? "get";
622
- const handler = eventHandler(async (event) => {
623
- event.context.rpcName = procedure.name;
624
- if (isPreflightRequest(event)) {
625
- return "ok";
626
- }
627
- try {
628
- if (opts.onRequest) {
629
- await opts.onRequest(event);
630
- }
631
- if (opts.middleware.length) {
632
- for (const m of opts.middleware) {
633
- await m(event);
634
- }
635
- }
636
- if (isRpcParamSchema(procedure.params)) {
637
- await validateRpcRequestInput(
638
- event,
639
- httpMethod,
640
- procedure.params
641
- );
642
- }
643
- const stream = new EventStreamConnection(event, {
644
- pingInterval: procedure.pingInterval,
645
- validator: responseValidator?.validate ?? function() {
646
- return true;
647
- },
648
- serializer: (
649
- // eslint-disable-next-line @typescript-eslint/unbound-method
650
- responseValidator?.serialize ?? function(_) {
651
- return "";
652
- }
653
- ),
654
- validationErrors(input) {
655
- if (procedure.response) {
656
- return a.errors(procedure.response, input);
657
- }
658
- return [];
659
- }
660
- });
661
- event.context.stream = stream;
662
- await procedure.handler(
663
- event.context,
664
- event
665
- );
666
- if (!event.handled && !stream.eventStream._handled) {
667
- stream.send();
668
- }
669
- } catch (err) {
670
- await handleH3Error(err, event, opts.onError, opts.debug ?? false);
671
- }
672
- return "";
673
- });
674
- switch (httpMethod) {
675
- case "get":
676
- router.get(path, handler);
677
- break;
678
- case "delete":
679
- router.delete(path, handler);
680
- break;
681
- case "patch":
682
- router.patch(path, handler);
683
- break;
684
- case "put":
685
- router.put(path, handler);
686
- break;
687
- case "post":
688
- default:
689
- router.post(path, handler);
690
- break;
691
- }
692
- }
693
-
694
- function defineRoute(route) {
695
- return route;
696
- }
697
- function registerRoute(router, route, opts) {
698
- if (typeof route.method === "string") {
699
- handleRoute(router, route.method, route, opts);
700
- return;
701
- }
702
- for (const method of route.method) {
703
- handleRoute(router, method, route, opts);
704
- }
705
- }
706
- function handleRoute(router, method, route, opts) {
707
- const handler = defineEventHandler(async (event) => {
708
- if (isPreflightRequest(event)) {
709
- return "ok";
710
- }
711
- try {
712
- if (opts.onRequest) {
713
- await opts.onRequest(event);
714
- }
715
- if (opts.middleware.length) {
716
- for (const m of opts.middleware) {
717
- await m(event);
718
- }
719
- }
720
- if (route.query) {
721
- const query = getQuery(event);
722
- const parsedQuery = a.safeCoerce(route.query, query);
723
- if (!parsedQuery.success) {
724
- const errParts = [];
725
- for (const err of parsedQuery.error.errors) {
726
- const errPath = err.instancePath.split("/");
727
- errPath.shift();
728
- const propName = errPath.join(".");
729
- if (!errParts.includes(propName)) {
730
- errParts.push(propName);
731
- }
732
- }
733
- const message = `Missing or invalid url query parameters: [${errParts.join(
734
- ", "
735
- )}]`;
736
- throw defineError(400, {
737
- message
738
- });
739
- }
740
- event.context.query = parsedQuery.value;
741
- }
742
- const notAllowedBodyMethods = [
743
- "GET",
744
- "HEAD",
745
- "CONNECT",
746
- "OPTIONS"
747
- ];
748
- if (route.body && !notAllowedBodyMethods.includes(event.method)) {
749
- const body = await readRawBody(event);
750
- const parsedBody = a.safeParse(route.body, body);
751
- if (!parsedBody.success) {
752
- const errorParts = [];
753
- for (const err of parsedBody.error.errors) {
754
- const errPath = err.instancePath.split("/");
755
- errPath.shift();
756
- if (!errorParts.includes(errPath.join("."))) {
757
- errorParts.push(errPath.join("."));
758
- }
759
- }
760
- throw defineError(400, {
761
- message: `Invalid request body. Affected properties [${errorParts.join(
762
- ", "
763
- )}]`,
764
- data: parsedBody.error
765
- });
766
- }
767
- event.context.body = parsedBody.value;
768
- }
769
- const response = await route.handler(event);
770
- event.context.response = response;
771
- if (!event.handled) {
772
- if (opts.onBeforeResponse) {
773
- await opts.onBeforeResponse(event);
774
- }
775
- if (typeof response === "object" && response) {
776
- setHeader(event, "Content-Type", "application/json");
777
- await send(event, JSON.stringify(response));
778
- } else {
779
- await send(event, response);
780
- }
781
- }
782
- if (opts.onAfterResponse) {
783
- await opts.onAfterResponse(event);
784
- }
785
- if (route.postHandler) {
786
- await route.postHandler(event);
787
- }
788
- } catch (err) {
789
- await handleH3Error(err, event, opts.onError, opts.debug ?? false);
790
- }
791
- return "";
792
- });
793
- switch (method) {
794
- case "head":
795
- router.head(route.path, handler);
796
- break;
797
- case "get":
798
- router.get(route.path, handler);
799
- break;
800
- case "delete":
801
- router.delete(route.path, handler);
802
- break;
803
- case "patch":
804
- router.patch(route.path, handler);
805
- break;
806
- case "post":
807
- router.post(route.path, handler);
808
- break;
809
- case "put":
810
- router.put(route.path, handler);
811
- break;
812
- }
813
- }
814
-
815
- var __defProp$2 = Object.defineProperty;
816
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
817
- var __publicField$2 = (obj, key, value) => {
818
- __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
819
- return value;
820
- };
821
- class ArriRouter {
822
- constructor() {
823
- __publicField$2(this, "procedures", []);
824
- __publicField$2(this, "routes", []);
825
- __publicField$2(this, "models", {});
826
- }
827
- rpc(procedure) {
828
- procedure.transport = "http";
829
- this.procedures.push(procedure);
830
- }
831
- wsRpc(procedure) {
832
- procedure.transport = "ws";
833
- this.procedures.push(procedure);
834
- }
835
- route(route) {
836
- this.routes.push(route);
837
- }
838
- registerModels(models) {
839
- for (const key of Object.keys(models)) {
840
- this.models[key] = models[key];
841
- }
842
- }
843
- getProcedures() {
844
- return this.procedures;
845
- }
846
- getRoutes() {
847
- return this.routes;
848
- }
849
- getModels() {
850
- return this.models;
851
- }
852
- }
853
-
854
- var __defProp$1 = Object.defineProperty;
855
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
856
- var __publicField$1 = (obj, key, value) => {
857
- __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
858
- return value;
859
- };
860
- class WsPeer {
861
- constructor(peer, opts) {
862
- __publicField$1(this, "_peer");
863
- __publicField$1(this, "_validator");
864
- __publicField$1(this, "context");
865
- __publicField$1(this, "url");
866
- this._peer = peer;
867
- this._validator = opts.validator;
868
- this.url = peer.url;
869
- this.context = opts.context;
870
- }
871
- send(data) {
872
- if (!this._validator) {
873
- return;
874
- }
875
- if (!this._validator.validate(data)) {
876
- return;
877
- }
878
- const payload = this._validator.serialize(data);
879
- return this._peer.send(`event: message
880
- data: ${payload}`);
881
- }
882
- sendError(err) {
883
- return this._peer.send(`event: error
884
- data: ${JSON.stringify(err)}`);
885
- }
886
- close() {
887
- throw new Error("close() is not yet implemented on WsPeer");
888
- }
889
- subscribe(channel) {
890
- this._peer.subscribe(channel);
891
- }
892
- unsubscribe(channel) {
893
- this._peer.unsubscribe(channel);
894
- }
895
- publish(channel, message) {
896
- if (!this._validator) {
897
- return;
898
- }
899
- if (!this._validator.validate(message)) {
900
- const err = {
901
- code: 500,
902
- message: `Error serializing message on server. The payload doesn't match the specified schema.`,
903
- data: {
904
- payload: message
905
- }
906
- };
907
- this.sendError(err);
908
- return;
909
- }
910
- const payload = this._validator.serialize(message);
911
- this._peer.publish(channel, payload);
912
- }
913
- readyState() {
914
- return this._peer.readyState;
915
- }
916
- }
917
- function defineWebsocketRpc(def) {
918
- def.transport = "ws";
919
- return def;
920
- }
921
- function registerWebsocketRpc(router, path, rpc) {
922
- let responseValidator;
923
- let paramValidator;
924
- try {
925
- responseValidator = rpc.response ? a.compile(rpc.response) : void 0;
926
- } catch (err) {
927
- console.error("ERROR COMPILING VALIDATOR", err);
928
- }
929
- try {
930
- paramValidator = rpc.params ? a.compile(rpc.params) : void 0;
931
- } catch (err) {
932
- console.error("ERROR COMPILING PARAMS", err);
933
- }
934
- const rpcHandler = typeof rpc.handler === "function" ? rpc.handler() : rpc.handler;
935
- const handler = defineWebSocketHandler({
936
- upgrade(req) {
937
- },
938
- open(peer) {
939
- const urlParts = peer.url.split("?");
940
- const context = {
941
- rpcName: rpc.name,
942
- clientAddress: peer.addr
943
- };
944
- if (urlParts.length > 1) {
945
- urlParts.shift();
946
- const queryStr = new URLSearchParams(urlParts.join("?"));
947
- const query = {};
948
- for (const [key, val] of queryStr.entries()) {
949
- query[key] = val;
950
- }
951
- context.queryParams = query;
952
- }
953
- const wsPeer = new WsPeer(peer, {
954
- validator: responseValidator,
955
- context
956
- });
957
- peer.ctx.__wsPeer = wsPeer;
958
- rpcHandler.onOpen(peer.ctx.__wsPeer);
959
- },
960
- message(peer, message) {
961
- if (!paramValidator) {
962
- return;
963
- }
964
- const data = paramValidator.safeParse(message.text());
965
- if (!data.success) {
966
- const errorResponse = {
967
- code: 400,
968
- message: data.error.message,
969
- data: data.error.errors,
970
- stack: data.error.stack?.split("\n")
971
- };
972
- peer.ctx.__wsPeer.sendError(errorResponse);
973
- return;
974
- }
975
- rpcHandler.onMessage(peer.ctx.__wsPeer, data.value);
976
- },
977
- close(peer, details) {
978
- rpcHandler.onClose(peer.ctx.__wsPeer, details);
979
- }
980
- });
981
- router.use(path, handler);
982
- }
983
- function createWsRpcDefinition(rpcName, path, rpc) {
984
- return {
985
- transport: "ws",
986
- path,
987
- params: getRpcParamName(rpcName, rpc),
988
- response: getRpcResponseName(rpcName, rpc),
989
- isDeprecated: rpc.isDeprecated,
990
- description: rpc.description
991
- };
992
- }
993
-
994
- var __defProp = Object.defineProperty;
995
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
996
- var __publicField = (obj, key, value) => {
997
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
998
- return value;
999
- };
1000
- const DEV_ENDPOINT_ROOT = `/__arri_dev__`;
1001
- const DEV_DEFINITION_ENDPOINT = `${DEV_ENDPOINT_ROOT}/__definition`;
1002
- const createAppDefinition = (def) => def;
1003
- class ArriApp {
1004
- constructor(opts = {}) {
1005
- __publicField(this, "__isArri__", true);
1006
- __publicField(this, "h3App");
1007
- __publicField(this, "h3Router", createRouter());
1008
- __publicField(this, "_rpcDefinitionPath");
1009
- __publicField(this, "_rpcRoutePrefix");
1010
- __publicField(this, "appInfo");
1011
- __publicField(this, "_procedures", {});
1012
- __publicField(this, "_models", {});
1013
- __publicField(this, "_middlewares", []);
1014
- __publicField(this, "_onRequest");
1015
- __publicField(this, "_onAfterResponse");
1016
- __publicField(this, "_onBeforeResponse");
1017
- __publicField(this, "_onError");
1018
- __publicField(this, "_debug");
1019
- __publicField(this, "definitionPath");
1020
- this.appInfo = opts?.appInfo;
1021
- this.h3App = createApp({
1022
- debug: opts?.debug
1023
- });
1024
- this._debug = opts.debug ?? false;
1025
- this._onRequest = opts.onRequest;
1026
- this._onError = opts.onError;
1027
- this._onAfterResponse = opts.onAfterResponse;
1028
- this._onBeforeResponse = opts.onBeforeResponse;
1029
- this._rpcRoutePrefix = opts?.rpcRoutePrefix ?? "";
1030
- this._rpcDefinitionPath = opts?.rpcDefinitionPath ?? "__definition";
1031
- this.h3App.use(this.h3Router);
1032
- this.definitionPath = this._rpcRoutePrefix ? `/${this._rpcRoutePrefix}/${this._rpcDefinitionPath}`.split("//").join("/") : `/${this._rpcDefinitionPath}`;
1033
- this.h3Router.get(
1034
- this.definitionPath,
1035
- eventHandler(() => this.getAppDefinition())
1036
- );
1037
- if (!opts.disableDefaultRoute) {
1038
- this.route({
1039
- method: ["get", "head"],
1040
- path: "/",
1041
- handler: (_) => {
1042
- let schemaPath;
1043
- if (this._rpcRoutePrefix) {
1044
- schemaPath = `/${this._rpcRoutePrefix}/${this._rpcDefinitionPath}`;
1045
- } else {
1046
- schemaPath = `/${this._rpcDefinitionPath}`;
1047
- }
1048
- return {
1049
- title: this.appInfo?.title ?? "Arri-RPC Server",
1050
- description: this.appInfo?.description ?? "This server utilizes Arri-RPC. Visit the schema path to see all of the available procedures.",
1051
- schemaPath,
1052
- ...this.appInfo
1053
- };
1054
- }
1055
- });
1056
- }
1057
- if (process.env.ARRI_DEV_MODE === "true") {
1058
- this.h3Router.get(
1059
- DEV_DEFINITION_ENDPOINT,
1060
- eventHandler(() => this.getAppDefinition())
1061
- );
1062
- }
1063
- this.h3Router.use(
1064
- "/**",
1065
- eventHandler(async (event) => {
1066
- setResponseStatus(event, 404);
1067
- const error = defineError(404);
1068
- try {
1069
- if (this._onRequest) {
1070
- await this._onRequest(event);
1071
- }
1072
- } catch (err) {
1073
- await handleH3Error(err, event, this._onError, this._debug);
1074
- }
1075
- if (event.handled) {
1076
- return;
1077
- }
1078
- return handleH3Error(error, event, this._onError, this._debug);
1079
- })
1080
- );
1081
- }
1082
- use(input) {
1083
- if (typeof input === "object" && input instanceof ArriRouter) {
1084
- for (const route of input.getRoutes()) {
1085
- this.route(route);
1086
- }
1087
- for (const rpc of input.getProcedures()) {
1088
- if (rpc.transport === "http") {
1089
- this.rpc(rpc);
1090
- } else {
1091
- this.wsRpc(rpc);
1092
- }
1093
- }
1094
- this.registerModels(input.getModels());
1095
- return;
1096
- }
1097
- this._middlewares.push(input);
1098
- }
1099
- rpc(procedure) {
1100
- procedure.transport = "http";
1101
- const p = procedure;
1102
- const path = p.path ?? getRpcPath(p.name, this._rpcRoutePrefix);
1103
- this._procedures[p.name] = createHttpRpcDefinition(p.name, path, p);
1104
- if (isRpcParamSchema(p.params)) {
1105
- const paramName = getRpcParamName(p.name, p);
1106
- if (paramName) {
1107
- this._models[paramName] = p.params;
1108
- }
1109
- }
1110
- if (isRpcParamSchema(p.response)) {
1111
- const responseName = getRpcResponseName(p.name, p);
1112
- if (responseName) {
1113
- this._models[responseName] = p.response;
1114
- }
1115
- }
1116
- if (isEventStreamRpc(p)) {
1117
- registerEventStreamRpc(this.h3Router, path, p, {
1118
- middleware: this._middlewares,
1119
- onRequest: this._onRequest,
1120
- onError: this._onError,
1121
- onAfterResponse: this._onAfterResponse,
1122
- onBeforeResponse: this._onBeforeResponse,
1123
- debug: this._debug
1124
- });
1125
- return;
1126
- }
1127
- registerRpc(this.h3Router, path, p, {
1128
- middleware: this._middlewares,
1129
- onRequest: this._onRequest,
1130
- onError: this._onError,
1131
- onAfterResponse: this._onAfterResponse,
1132
- onBeforeResponse: this._onBeforeResponse,
1133
- debug: this._debug
1134
- });
1135
- }
1136
- wsRpc(procedure) {
1137
- procedure.transport = "ws";
1138
- const p = procedure;
1139
- const path = procedure.path ?? getRpcPath(procedure.name, this._rpcRoutePrefix);
1140
- this._procedures[procedure.name] = createWsRpcDefinition(
1141
- procedure.name,
1142
- path,
1143
- p
1144
- );
1145
- if (isRpcParamSchema(procedure.params)) {
1146
- const paramName = getRpcParamName(procedure.name, p);
1147
- if (paramName) {
1148
- this._models[paramName] = procedure.params;
1149
- }
1150
- }
1151
- if (isRpcParamSchema(procedure.response)) {
1152
- const responseName = getRpcResponseName(procedure.name, p);
1153
- if (responseName) {
1154
- this._models[responseName] = procedure.response;
1155
- }
1156
- }
1157
- registerWebsocketRpc(this.h3Router, path, p);
1158
- }
1159
- route(route) {
1160
- registerRoute(this.h3Router, route, {
1161
- middleware: this._middlewares,
1162
- onRequest: this._onRequest,
1163
- onError: this._onError,
1164
- onAfterResponse: this._onAfterResponse,
1165
- onBeforeResponse: this._onBeforeResponse,
1166
- debug: this._debug
1167
- });
1168
- }
1169
- registerModels(models) {
1170
- for (const key of Object.keys(models)) {
1171
- this._models[key] = models[key];
1172
- }
1173
- }
1174
- getAppDefinition() {
1175
- const appDef = {
1176
- arriSchemaVersion: SCHEMA_VERSION,
1177
- info: this.appInfo,
1178
- procedures: {},
1179
- models: this._models
1180
- };
1181
- for (const key of Object.keys(this._procedures)) {
1182
- const rpc = this._procedures[key];
1183
- appDef.procedures[key] = rpc;
1184
- }
1185
- return appDef;
1186
- }
1187
- }
1188
-
1189
- function isArriConfig(input) {
1190
- if (typeof input !== "object" || input === null) {
1191
- return false;
1192
- }
1193
- if ("generators" in input) {
1194
- if (!Array.isArray(input.generators)) {
1195
- return false;
1196
- }
1197
- }
1198
- if ("port" in input) {
1199
- if (typeof input.port !== "number") {
1200
- return false;
1201
- }
1202
- if (Number.isNaN(input.port)) {
1203
- return false;
1204
- }
1205
- }
1206
- if ("entry" in input && typeof input.entry !== "string") {
1207
- return false;
1208
- }
1209
- return true;
1210
- }
1211
- function isResolvedArriConfig(input) {
1212
- if (!isArriConfig(input)) {
1213
- return false;
1214
- }
1215
- return typeof input.rootDir === "string" && typeof input.srcDir === "string" && (typeof input.procedureDir === "string" || typeof input.procedureDir === "boolean") && Array.isArray(input.procedureGlobPatterns) && Array.isArray(input.generators);
1216
- }
1217
- const defaultConfig = {
1218
- port: 3e3,
1219
- rootDir: ".",
1220
- srcDir: "src",
1221
- entry: "app.ts",
1222
- serverEntry: "",
1223
- procedureDir: "procedures",
1224
- procedureGlobPatterns: ["**/*.rpc.ts"],
1225
- generators: [],
1226
- buildDir: ".arri",
1227
- esbuild: {},
1228
- https: false,
1229
- http2: false,
1230
- devServer: {}
1231
- };
1232
- function defineConfig(config) {
1233
- return {
1234
- port: config.port ?? defaultConfig.port,
1235
- rootDir: config.rootDir ?? defaultConfig.rootDir,
1236
- srcDir: config.srcDir ?? defaultConfig.srcDir,
1237
- entry: config.entry ?? defaultConfig.entry,
1238
- procedureDir: config.procedureDir ?? defaultConfig.procedureDir,
1239
- procedureGlobPatterns: config.procedureGlobPatterns ?? defaultConfig.procedureGlobPatterns,
1240
- generators: config.generators ?? defaultConfig.generators,
1241
- buildDir: config.buildDir ?? defaultConfig.buildDir,
1242
- esbuild: config.esbuild ?? defaultConfig.esbuild,
1243
- serverEntry: config.serverEntry ?? "",
1244
- https: config.https ?? false,
1245
- http2: config.http2 ?? false,
1246
- devServer: config.devServer ?? {}
1247
- };
1248
- }
1249
-
1250
- export { ArriApp as A, registerEventStreamRpc as B, defineWebsocketRpc as C, DEV_ENDPOINT_ROOT as D, EventStreamConnection as E, registerWebsocketRpc as F, createWsRpcDefinition as G, WsPeer as W, DEV_DEFINITION_ENDPOINT as a, isRpc as b, createAppDefinition as c, defineRpc as d, createHttpRpcDefinition as e, getRpcParamName as f, getRpcPath as g, getRpcResponseName as h, isRpcParamSchema as i, ArriServerError as j, ArriServerErrorResponse as k, defineError as l, errorResponseFromValidationErrors as m, handleH3Error as n, isArriConfig as o, isResolvedArriConfig as p, defaultConfig as q, registerRpc as r, defineConfig as s, ArriRouter as t, defineRoute as u, validateRpcRequestInput as v, registerRoute as w, handleRoute as x, defineEventStreamRpc as y, isEventStreamRpc as z };