@arrirpc/server 0.55.0 → 0.57.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
@@ -189,13 +189,17 @@ For those that want to opt out of the file-based routing system you can manually
189
189
  ```ts
190
190
  // using the app instance
191
191
  const app = new ArriApp()
192
- app.rpc('sayHello', {...})
192
+ app.rpc('sayHello',
193
+ defineRpc({...})
194
+ );
193
195
 
194
- // using a sub-router
196
+ // defining a service
195
197
  const app = new ArriApp();
196
- const router = new ArriRouter();
197
- router.rpc('sayHello', {...})
198
- app.use(router)
198
+ const usersService = defineService("users", {
199
+ getUser: defineRpc({..}),
200
+ createUser: defineRpc({..}),
201
+ });
202
+ app.use(usersService);
199
203
  ```
200
204
 
201
205
  #### Creating Event Stream Procedures
@@ -205,7 +209,6 @@ Event stream procedures make use of [Server Sent Events](https://developer.mozil
205
209
  Arri Event streams sent the following event types:
206
210
 
207
211
  - `message` - A standard message with the response data serialized as JSON
208
- - `error` - An error message with an `ArriRequestError` sent as JSON
209
212
  - `done` - A message to tell clients that there will be no more events
210
213
  - `ping` - A message periodically sent by the server to keep the connection alive.
211
214
 
@@ -215,11 +218,6 @@ id: string | undefined;
215
218
  event: "message";
216
219
  data: Response; // whatever you have specified as the response serialized to json
217
220
 
218
- /// error event ///
219
- id: string | undefined;
220
- event: "error";
221
- data: ArriRequestError; // serialized to json
222
-
223
221
  /// done event ///
224
222
  event: "done";
225
223
  data: "this stream has ended";
@@ -268,11 +266,14 @@ export default defineEventStreamRpc({
268
266
  #### EventStreamConnection methods
269
267
 
270
268
  ```ts
271
- stream.push(data: Data, eventId?: string)
272
- stream.pushError(error: ArriRequestError, eventId?: string)
269
+ // send the stream to the client. Must be called before pushing any messages
273
270
  stream.send()
274
- stream.end()
275
- stream.on(e: 'request:close' | 'close', callback: () => any)
271
+ // push a new message to the client
272
+ stream.push(data: Data, eventId?: string)
273
+ // close the stream and tell the client that there will be no more messages
274
+ stream.close()
275
+ // register a callback that will fire after the stream has been close by the server or the connection has been dropped
276
+ stream.onClosed(cb: () => any)
276
277
  ```
277
278
 
278
279
  ### Creating Websocket Procedures (Experimental)
@@ -363,6 +364,16 @@ router.route({
363
364
  }
364
365
  })
365
366
  app.use(router)
367
+
368
+ // sup-routers can also specify a route prefix
369
+ const router = new ArriRouter("/v1")
370
+ router.route({
371
+ method: "get",
372
+ path: "/hello-world", // this will become /v1/hello-world
373
+ handler(event) {
374
+ return "hello world"
375
+ }
376
+ });
366
377
  ```
367
378
 
368
379
  ### Adding Middleware
@@ -440,7 +451,7 @@ export default defineConfig({
440
451
 
441
452
  For info on what generators are available see [here](/README.md#client-generators)
442
453
 
443
- For info on how to create your own generator see []
454
+ For info on how to create your own generator see [@arrirpc/codegen-utils](/tooling/codegen-utils/README.md)
444
455
 
445
456
  ## Key Concepts
446
457
 
package/dist/index.cjs CHANGED
@@ -6,17 +6,17 @@ const h3 = require('h3');
6
6
  const schema = require('@arrirpc/schema');
7
7
  const scule = require('scule');
8
8
 
9
- var __defProp$4 = Object.defineProperty;
10
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
- var __publicField$4 = (obj, key, value) => {
12
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
9
+ var __defProp$5 = Object.defineProperty;
10
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __publicField$5 = (obj, key, value) => {
12
+ __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
13
13
  return value;
14
14
  };
15
15
  class ArriServerError extends Error {
16
16
  constructor(err) {
17
17
  super(err.message);
18
- __publicField$4(this, "code");
19
- __publicField$4(this, "data");
18
+ __publicField$5(this, "code");
19
+ __publicField$5(this, "data");
20
20
  this.code = err.code;
21
21
  this.data = err.data;
22
22
  if (err.stack) {
@@ -324,12 +324,8 @@ function getRpcResponseDefinition(rpcName, procedure) {
324
324
  return name;
325
325
  }
326
326
  function registerRpc(router, path, procedure, opts) {
327
- let responseValidator;
328
- try {
329
- responseValidator = procedure.response ? schema.a.compile(procedure.response) : void 0;
330
- } catch (err) {
331
- console.error("ERROR COMPILING VALIDATOR", err);
332
- }
327
+ const paramValidator = procedure.params ? getSchemaValidator(procedure.name, "params", procedure.params) : void 0;
328
+ const responseValidator = procedure.response ? getSchemaValidator(procedure.name, "response", procedure.response) : void 0;
333
329
  const httpMethod = procedure.method ?? "post";
334
330
  const handler = h3.eventHandler(async (event) => {
335
331
  event.context.rpcName = procedure.name;
@@ -349,7 +345,8 @@ function registerRpc(router, path, procedure, opts) {
349
345
  await validateRpcRequestInput(
350
346
  event,
351
347
  httpMethod,
352
- procedure.params
348
+ procedure.params,
349
+ paramValidator
353
350
  );
354
351
  }
355
352
  const response = await procedure.handler(
@@ -412,7 +409,7 @@ function registerRpc(router, path, procedure, opts) {
412
409
  break;
413
410
  }
414
411
  }
415
- async function validateRpcRequestInput(event, httpMethod, schema$1) {
412
+ async function validateRpcRequestInput(event, httpMethod, schema$1, validator) {
416
413
  switch (httpMethod) {
417
414
  case "get": {
418
415
  const parsedParams = await h3.getValidatedQuery(
@@ -451,8 +448,8 @@ async function validateRpcRequestInput(event, httpMethod, schema$1) {
451
448
  message: `Invalid request body. Expected object. Got undefined.`
452
449
  });
453
450
  }
454
- const parsedParams = schema.a.safeParse(schema$1, body);
455
- if (!parsedParams.success) {
451
+ const parsedParams = validator.safeParse(body);
452
+ if (!parsedParams?.success) {
456
453
  const errorParts = [];
457
454
  for (const err of parsedParams.error.errors) {
458
455
  const errPath = err.instancePath.split("/");
@@ -473,11 +470,22 @@ async function validateRpcRequestInput(event, httpMethod, schema$1) {
473
470
  }
474
471
  }
475
472
  }
473
+ function getSchemaValidator(rpcName, type, schema$1) {
474
+ try {
475
+ if (schema.isAdaptedSchema(schema$1)) {
476
+ return schema.validatorFromAdaptedSchema(schema$1);
477
+ }
478
+ return schema.a.compile(schema$1);
479
+ } catch (err) {
480
+ console.error(`Error compiling ${type} validator for ${rpcName}`);
481
+ return void 0;
482
+ }
483
+ }
476
484
 
477
- var __defProp$3 = Object.defineProperty;
478
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
479
- var __publicField$3 = (obj, key, value) => {
480
- __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
485
+ var __defProp$4 = Object.defineProperty;
486
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
487
+ var __publicField$4 = (obj, key, value) => {
488
+ __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
481
489
  return value;
482
490
  };
483
491
  function defineEventStreamRpc(config) {
@@ -493,14 +501,14 @@ function isEventStreamRpc(input) {
493
501
  }
494
502
  class EventStreamConnection {
495
503
  constructor(event, opts) {
496
- __publicField$3(this, "lastEventId");
497
- __publicField$3(this, "validationErrors");
498
- __publicField$3(this, "validator");
499
- __publicField$3(this, "serializer");
504
+ __publicField$4(this, "lastEventId");
505
+ __publicField$4(this, "validationErrors");
506
+ __publicField$4(this, "validator");
507
+ __publicField$4(this, "serializer");
500
508
  // for some reason Rollup cannot output DTS when this is set to NodeJS.Timeout
501
- __publicField$3(this, "pingInterval");
502
- __publicField$3(this, "pingIntervalMs");
503
- __publicField$3(this, "eventStream");
509
+ __publicField$4(this, "pingInterval");
510
+ __publicField$4(this, "pingIntervalMs");
511
+ __publicField$4(this, "eventStream");
504
512
  this.eventStream = h3.createEventStream(event);
505
513
  this.lastEventId = h3.getHeader(event, "Last-Event-Id");
506
514
  this.pingIntervalMs = opts.pingInterval ?? 6e4;
@@ -580,12 +588,8 @@ class EventStreamConnection {
580
588
  }
581
589
  }
582
590
  function registerEventStreamRpc(router, path, procedure, opts) {
583
- let responseValidator;
584
- try {
585
- responseValidator = procedure.response ? schema.a.compile(procedure.response) : void 0;
586
- } catch (err) {
587
- console.error("ERROR COMPILING VALIDATOR", err);
588
- }
591
+ const paramValidator = procedure.params ? getSchemaValidator(procedure.name, "params", procedure.params) : void 0;
592
+ const responseValidator = procedure.response ? getSchemaValidator(procedure.name, "response", procedure.response) : void 0;
589
593
  const httpMethod = procedure.method ?? "get";
590
594
  const handler = h3.eventHandler(async (event) => {
591
595
  event.context.rpcName = procedure.name;
@@ -605,7 +609,8 @@ function registerEventStreamRpc(router, path, procedure, opts) {
605
609
  await validateRpcRequestInput(
606
610
  event,
607
611
  httpMethod,
608
- procedure.params
612
+ procedure.params,
613
+ paramValidator
609
614
  );
610
615
  }
611
616
  const stream = new EventStreamConnection(event, {
@@ -780,29 +785,53 @@ function handleRoute(router, method, route, opts) {
780
785
  }
781
786
  }
782
787
 
788
+ var __defProp$3 = Object.defineProperty;
789
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
790
+ var __publicField$3 = (obj, key, value) => {
791
+ __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
792
+ return value;
793
+ };
794
+ class ArriRouter {
795
+ constructor(routePrefix = "") {
796
+ __publicField$3(this, "routes", []);
797
+ __publicField$3(this, "definitions", {});
798
+ __publicField$3(this, "prefix");
799
+ this.prefix = routePrefix;
800
+ }
801
+ route(route) {
802
+ route.path = `${this.prefix}${route.path}`;
803
+ this.routes.push(route);
804
+ }
805
+ registerDefinitions(models) {
806
+ for (const key of Object.keys(models)) {
807
+ this.definitions[key] = models[key];
808
+ }
809
+ }
810
+ getRoutes() {
811
+ return this.routes;
812
+ }
813
+ getDefinitions() {
814
+ return this.definitions;
815
+ }
816
+ }
817
+
783
818
  var __defProp$2 = Object.defineProperty;
784
819
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
785
820
  var __publicField$2 = (obj, key, value) => {
786
821
  __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
787
822
  return value;
788
823
  };
789
- class ArriRouter {
790
- constructor() {
824
+ class ArriService {
825
+ constructor(name) {
826
+ __publicField$2(this, "name");
791
827
  __publicField$2(this, "procedures", []);
792
- __publicField$2(this, "routes", []);
793
828
  __publicField$2(this, "definitions", {});
829
+ this.name = name;
794
830
  }
795
- rpc(procedure) {
796
- procedure.transport = "http";
797
- this.procedures.push(procedure);
798
- }
799
- wsRpc(procedure) {
800
- procedure.transport = "ws";
831
+ rpc(name, procedure) {
832
+ procedure.name = `${this.name}.${name}`;
801
833
  this.procedures.push(procedure);
802
834
  }
803
- route(route) {
804
- this.routes.push(route);
805
- }
806
835
  registerDefinitions(models) {
807
836
  for (const key of Object.keys(models)) {
808
837
  this.definitions[key] = models[key];
@@ -811,13 +840,17 @@ class ArriRouter {
811
840
  getProcedures() {
812
841
  return this.procedures;
813
842
  }
814
- getRoutes() {
815
- return this.routes;
816
- }
817
843
  getDefinitions() {
818
844
  return this.definitions;
819
845
  }
820
846
  }
847
+ function defineService(name, procedures) {
848
+ const service = new ArriService(name);
849
+ for (const key of Object.keys(procedures ?? {})) {
850
+ service.rpc(key, procedures[key]);
851
+ }
852
+ return service;
853
+ }
821
854
 
822
855
  var __defProp$1 = Object.defineProperty;
823
856
  var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -999,14 +1032,21 @@ class ArriApp {
999
1032
  if (!opts.disableDefinitionRoute) {
1000
1033
  this.h3Router.get(
1001
1034
  this.definitionPath,
1002
- h3.eventHandler(() => this.getAppDefinition())
1035
+ h3.eventHandler((event) => {
1036
+ h3.setResponseHeader(
1037
+ event,
1038
+ "Content-Type",
1039
+ "application/json"
1040
+ );
1041
+ return this.getAppDefinition();
1042
+ })
1003
1043
  );
1004
1044
  }
1005
1045
  if (!opts.disableDefaultRoute) {
1006
1046
  this.route({
1007
1047
  method: ["get", "head"],
1008
1048
  path: "/",
1009
- handler: (_) => {
1049
+ handler: async (_) => {
1010
1050
  const response = {
1011
1051
  title: this.appInfo?.title ?? "Arri-RPC Server",
1012
1052
  description: this.appInfo?.description ?? "This server utilizes Arri-RPC. Visit the schema path to see all of the available procedures.",
@@ -1029,7 +1069,14 @@ class ArriApp {
1029
1069
  if (process.env.ARRI_DEV_MODE === "true") {
1030
1070
  this.h3Router.get(
1031
1071
  arri.DEV_DEFINITION_ENDPOINT,
1032
- h3.eventHandler(() => this.getAppDefinition())
1072
+ h3.eventHandler((event) => {
1073
+ h3.setResponseHeader(
1074
+ event,
1075
+ "Content-Type",
1076
+ "application/json"
1077
+ );
1078
+ return this.getAppDefinition();
1079
+ })
1033
1080
  );
1034
1081
  }
1035
1082
  this.h3Router.use(
@@ -1056,23 +1103,27 @@ class ArriApp {
1056
1103
  for (const route of input.getRoutes()) {
1057
1104
  this.route(route);
1058
1105
  }
1106
+ this.registerDefinitions(input.getDefinitions());
1107
+ return;
1108
+ }
1109
+ if (typeof input === "object" && input instanceof ArriService) {
1059
1110
  for (const rpc of input.getProcedures()) {
1060
- if (rpc.transport === "http") {
1061
- this.rpc(rpc);
1062
- } else {
1063
- this.wsRpc(rpc);
1064
- }
1111
+ this.rpc(rpc.name, rpc);
1065
1112
  }
1066
1113
  this.registerDefinitions(input.getDefinitions());
1067
1114
  return;
1068
1115
  }
1069
1116
  this._middlewares.push(input);
1070
1117
  }
1071
- rpc(procedure) {
1072
- procedure.transport = "http";
1118
+ rpc(name, procedure) {
1119
+ procedure.name = name;
1073
1120
  const p = procedure;
1074
1121
  const path = p.path ?? getRpcPath(p.name, this._rpcRoutePrefix);
1075
- this._procedures[p.name] = createHttpRpcDefinition(p.name, path, p);
1122
+ if (p.transport === "http") {
1123
+ this._procedures[p.name] = createHttpRpcDefinition(p.name, path, p);
1124
+ } else if (p.transport === "ws") {
1125
+ this._procedures[p.name] = createWsRpcDefinition(p.name, path, p);
1126
+ }
1076
1127
  if (isRpcParamSchema(p.params)) {
1077
1128
  const paramName = getRpcParamName(p.name, p);
1078
1129
  if (paramName) {
@@ -1085,8 +1136,19 @@ class ArriApp {
1085
1136
  this._definitions[responseName] = p.response;
1086
1137
  }
1087
1138
  }
1088
- if (isEventStreamRpc(p)) {
1089
- registerEventStreamRpc(this.h3Router, path, p, {
1139
+ if (p.transport === "http") {
1140
+ if (isEventStreamRpc(p)) {
1141
+ registerEventStreamRpc(this.h3Router, path, p, {
1142
+ middleware: this._middlewares,
1143
+ onRequest: this._onRequest,
1144
+ onError: this._onError,
1145
+ onAfterResponse: this._onAfterResponse,
1146
+ onBeforeResponse: this._onBeforeResponse,
1147
+ debug: this._debug
1148
+ });
1149
+ return;
1150
+ }
1151
+ registerRpc(this.h3Router, path, p, {
1090
1152
  middleware: this._middlewares,
1091
1153
  onRequest: this._onRequest,
1092
1154
  onError: this._onError,
@@ -1096,37 +1158,9 @@ class ArriApp {
1096
1158
  });
1097
1159
  return;
1098
1160
  }
1099
- registerRpc(this.h3Router, path, p, {
1100
- middleware: this._middlewares,
1101
- onRequest: this._onRequest,
1102
- onError: this._onError,
1103
- onAfterResponse: this._onAfterResponse,
1104
- onBeforeResponse: this._onBeforeResponse,
1105
- debug: this._debug
1106
- });
1107
- }
1108
- wsRpc(procedure) {
1109
- procedure.transport = "ws";
1110
- const p = procedure;
1111
- const path = procedure.path ?? getRpcPath(procedure.name, this._rpcRoutePrefix);
1112
- this._procedures[procedure.name] = createWsRpcDefinition(
1113
- procedure.name,
1114
- path,
1115
- p
1116
- );
1117
- if (isRpcParamSchema(procedure.params)) {
1118
- const paramName = getRpcParamName(procedure.name, p);
1119
- if (paramName) {
1120
- this._definitions[paramName] = procedure.params;
1121
- }
1122
- }
1123
- if (isRpcParamSchema(procedure.response)) {
1124
- const responseName = getRpcResponseName(procedure.name, p);
1125
- if (responseName) {
1126
- this._definitions[responseName] = procedure.response;
1127
- }
1161
+ if (p.transport === "ws") {
1162
+ registerWebsocketRpc(this.h3Router, path, p);
1128
1163
  }
1129
- registerWebsocketRpc(this.h3Router, path, p);
1130
1164
  }
1131
1165
  route(route) {
1132
1166
  registerRoute(this.h3Router, route, {
@@ -1164,6 +1198,7 @@ exports.ArriApp = ArriApp;
1164
1198
  exports.ArriRouter = ArriRouter;
1165
1199
  exports.ArriServerError = ArriServerError;
1166
1200
  exports.ArriServerErrorResponse = ArriServerErrorResponse;
1201
+ exports.ArriService = ArriService;
1167
1202
  exports.EventStreamConnection = EventStreamConnection;
1168
1203
  exports.WsPeer = WsPeer;
1169
1204
  exports.createAppDefinition = createAppDefinition;
@@ -1174,11 +1209,13 @@ exports.defineEventStreamRpc = defineEventStreamRpc;
1174
1209
  exports.defineMiddleware = defineMiddleware;
1175
1210
  exports.defineRoute = defineRoute;
1176
1211
  exports.defineRpc = defineRpc;
1212
+ exports.defineService = defineService;
1177
1213
  exports.defineWebsocketRpc = defineWebsocketRpc;
1178
1214
  exports.errorResponseFromValidationErrors = errorResponseFromValidationErrors;
1179
1215
  exports.getRpcParamName = getRpcParamName;
1180
1216
  exports.getRpcPath = getRpcPath;
1181
1217
  exports.getRpcResponseName = getRpcResponseName;
1218
+ exports.getSchemaValidator = getSchemaValidator;
1182
1219
  exports.handleH3Error = handleH3Error;
1183
1220
  exports.handleRoute = handleRoute;
1184
1221
  exports.isEventStreamRpc = isEventStreamRpc;