arri 0.1.0-beta.0 → 0.1.0-beta.10

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/dist/cli.cjs CHANGED
@@ -11,7 +11,7 @@ const consola = require('consola');
11
11
  const esbuild = require('esbuild');
12
12
  const path = require('pathe');
13
13
  const prettier = require('prettier');
14
- const config = require('./shared/arri.406d936a.cjs');
14
+ const config = require('./shared/arri.ed4bd5e1.cjs');
15
15
  const globby = require('globby');
16
16
  const scule = require('scule');
17
17
  const arriCodegenDart = require('arri-codegen-dart');
@@ -142,12 +142,12 @@ const getRpcMetaFromPath = (config, filePath) => {
142
142
  httpPath: `/${httpParts.join("/")}`
143
143
  };
144
144
  };
145
- async function transpileFilesContext(config) {
145
+ async function transpileFiles(config) {
146
146
  const outDir = path__default.resolve(config.rootDir, config.buildDir);
147
147
  const files = await globby.globby(["**/*.ts"], {
148
148
  cwd: path__default.resolve(config.rootDir, config.srcDir)
149
149
  });
150
- const ctx = await esbuild__namespace.context({
150
+ await esbuild__namespace.build({
151
151
  entryPoints: [
152
152
  ...files.map(
153
153
  (file) => path__default.resolve(config.rootDir, config.srcDir, file)
@@ -159,7 +159,6 @@ async function transpileFilesContext(config) {
159
159
  target: "node18",
160
160
  platform: "node"
161
161
  });
162
- return ctx;
163
162
  }
164
163
 
165
164
  const build = citty.defineCommand({
@@ -195,8 +194,7 @@ async function startBuild(config, skipCodeGen = false) {
195
194
  createBuildEntryModule(config),
196
195
  createBuildCodegenModule(config)
197
196
  ]);
198
- const transpile = await transpileFilesContext(config);
199
- await transpile.rebuild();
197
+ await transpileFiles(config);
200
198
  await bundleFiles(config);
201
199
  logger$1.log("Finished bundling");
202
200
  const clientCount = config.clientGenerators.length;
@@ -224,7 +222,6 @@ async function startBuild(config, skipCodeGen = false) {
224
222
  logger$1.log(
225
223
  `Build finished! You can start your server by running "node .output/server.js"`
226
224
  );
227
- await transpile.dispose();
228
225
  }
229
226
  async function bundleFiles(config, allowCodegen = true) {
230
227
  if (allowCodegen) {
@@ -263,6 +260,7 @@ async function bundleFiles(config, allowCodegen = true) {
263
260
  js: `import { createRequire as topLevelCreateRequire } from 'module';
264
261
  const require = topLevelCreateRequire(import.meta.url);`
265
262
  },
263
+ allowOverwrite: true,
266
264
  outdir: path__default.resolve(config.rootDir, ".output")
267
265
  });
268
266
  }
@@ -309,6 +307,7 @@ async function createBuildCodegenModule(config) {
309
307
  app.rpc({
310
308
  name: route.id,
311
309
  method: route.route.method,
310
+ path: route.route.path,
312
311
  params: route.route.params,
313
312
  response: route.route.response,
314
313
  handler: route.route.handler,
@@ -427,7 +426,7 @@ const dev = citty.defineCommand({
427
426
  await startDevServer(config$1);
428
427
  }
429
428
  });
430
- const startListener = (config, showQr = false) => listhen.listenAndWatch(path__default.resolve(config.rootDir, ".output", "bundle.js"), {
429
+ const startListener = (config, showQr = false) => listhen.listenAndWatch(path__default.resolve(config.rootDir, ".output", "server.js"), {
431
430
  public: true,
432
431
  port: config.port,
433
432
  logger,
@@ -439,7 +438,7 @@ async function bundleFilesContext(config) {
439
438
  entryPoints: [
440
439
  path__default.resolve(config.rootDir, config.buildDir, "entry.js")
441
440
  ],
442
- outfile: path__default.resolve(config.rootDir, ".output", "bundle.js"),
441
+ outfile: path__default.resolve(config.rootDir, ".output", "server.js"),
443
442
  format: "esm",
444
443
  bundle: true,
445
444
  sourcemap: true,
@@ -452,9 +451,8 @@ async function startDevServer(config) {
452
451
  await setupWorkingDir(config);
453
452
  let fileWatcher;
454
453
  await Promise.all([createRoutesModule(config), createEntryModule(config)]);
455
- const transpileFiles = await transpileFilesContext(config);
454
+ await transpileFiles(config);
456
455
  const bundleFiles = await bundleFilesContext(config);
457
- await transpileFiles.rebuild();
458
456
  await bundleFiles.rebuild();
459
457
  const listener = await startListener(config, true);
460
458
  setTimeout(async () => {
@@ -467,7 +465,6 @@ async function startDevServer(config) {
467
465
  await Promise.allSettled([
468
466
  listener.close(),
469
467
  fileWatcher?.close(),
470
- transpileFiles.dispose(),
471
468
  bundleFiles.dispose()
472
469
  ]);
473
470
  });
@@ -488,14 +485,17 @@ async function startDevServer(config) {
488
485
  ignored: [buildDir, outDir],
489
486
  ignoreInitial: true
490
487
  });
491
- fileWatcher.on("all", async (_event, file) => {
488
+ fileWatcher.on("all", async (event, file) => {
489
+ if (event === "add" || event === "addDir") {
490
+ return;
491
+ }
492
492
  await createRoutesModule(config);
493
493
  let bundleCreated = false;
494
494
  try {
495
495
  logger.log(
496
496
  "Change detected. Bundling files and restarting server...."
497
497
  );
498
- await transpileFiles.rebuild();
498
+ await transpileFiles(config);
499
499
  await bundleFiles.rebuild();
500
500
  bundleCreated = true;
501
501
  } catch (err) {
@@ -549,6 +549,7 @@ for (const route of routes) {
549
549
  app.rpc({
550
550
  name: route.id,
551
551
  method: route.route.method,
552
+ path: route.route.path,
552
553
  params: route.route.params,
553
554
  response: route.route.response,
554
555
  handler: route.route.handler,
package/dist/cli.mjs CHANGED
@@ -10,7 +10,7 @@ import * as esbuild from 'esbuild';
10
10
  import { build as build$1 } from 'esbuild';
11
11
  import path from 'pathe';
12
12
  import prettier from 'prettier';
13
- import { m as defaultConfig, l as isResolvedArriConfig, a as DEV_DEFINITION_ENDPOINT } from './shared/arri.8539c7bd.mjs';
13
+ import { m as defaultConfig, l as isResolvedArriConfig, a as DEV_DEFINITION_ENDPOINT } from './shared/arri.121d68e9.mjs';
14
14
  import { globby } from 'globby';
15
15
  import { camelCase, kebabCase } from 'scule';
16
16
  import { dartClientGenerator } from 'arri-codegen-dart';
@@ -121,12 +121,12 @@ const getRpcMetaFromPath = (config, filePath) => {
121
121
  httpPath: `/${httpParts.join("/")}`
122
122
  };
123
123
  };
124
- async function transpileFilesContext(config) {
124
+ async function transpileFiles(config) {
125
125
  const outDir = path.resolve(config.rootDir, config.buildDir);
126
126
  const files = await globby(["**/*.ts"], {
127
127
  cwd: path.resolve(config.rootDir, config.srcDir)
128
128
  });
129
- const ctx = await esbuild.context({
129
+ await esbuild.build({
130
130
  entryPoints: [
131
131
  ...files.map(
132
132
  (file) => path.resolve(config.rootDir, config.srcDir, file)
@@ -138,7 +138,6 @@ async function transpileFilesContext(config) {
138
138
  target: "node18",
139
139
  platform: "node"
140
140
  });
141
- return ctx;
142
141
  }
143
142
 
144
143
  const build = defineCommand({
@@ -174,8 +173,7 @@ async function startBuild(config, skipCodeGen = false) {
174
173
  createBuildEntryModule(config),
175
174
  createBuildCodegenModule(config)
176
175
  ]);
177
- const transpile = await transpileFilesContext(config);
178
- await transpile.rebuild();
176
+ await transpileFiles(config);
179
177
  await bundleFiles(config);
180
178
  logger$1.log("Finished bundling");
181
179
  const clientCount = config.clientGenerators.length;
@@ -203,7 +201,6 @@ async function startBuild(config, skipCodeGen = false) {
203
201
  logger$1.log(
204
202
  `Build finished! You can start your server by running "node .output/server.js"`
205
203
  );
206
- await transpile.dispose();
207
204
  }
208
205
  async function bundleFiles(config, allowCodegen = true) {
209
206
  if (allowCodegen) {
@@ -242,6 +239,7 @@ async function bundleFiles(config, allowCodegen = true) {
242
239
  js: `import { createRequire as topLevelCreateRequire } from 'module';
243
240
  const require = topLevelCreateRequire(import.meta.url);`
244
241
  },
242
+ allowOverwrite: true,
245
243
  outdir: path.resolve(config.rootDir, ".output")
246
244
  });
247
245
  }
@@ -288,6 +286,7 @@ async function createBuildCodegenModule(config) {
288
286
  app.rpc({
289
287
  name: route.id,
290
288
  method: route.route.method,
289
+ path: route.route.path,
291
290
  params: route.route.params,
292
291
  response: route.route.response,
293
292
  handler: route.route.handler,
@@ -406,7 +405,7 @@ const dev = defineCommand({
406
405
  await startDevServer(config);
407
406
  }
408
407
  });
409
- const startListener = (config, showQr = false) => listenAndWatch(path.resolve(config.rootDir, ".output", "bundle.js"), {
408
+ const startListener = (config, showQr = false) => listenAndWatch(path.resolve(config.rootDir, ".output", "server.js"), {
410
409
  public: true,
411
410
  port: config.port,
412
411
  logger,
@@ -418,7 +417,7 @@ async function bundleFilesContext(config) {
418
417
  entryPoints: [
419
418
  path.resolve(config.rootDir, config.buildDir, "entry.js")
420
419
  ],
421
- outfile: path.resolve(config.rootDir, ".output", "bundle.js"),
420
+ outfile: path.resolve(config.rootDir, ".output", "server.js"),
422
421
  format: "esm",
423
422
  bundle: true,
424
423
  sourcemap: true,
@@ -431,9 +430,8 @@ async function startDevServer(config) {
431
430
  await setupWorkingDir(config);
432
431
  let fileWatcher;
433
432
  await Promise.all([createRoutesModule(config), createEntryModule(config)]);
434
- const transpileFiles = await transpileFilesContext(config);
433
+ await transpileFiles(config);
435
434
  const bundleFiles = await bundleFilesContext(config);
436
- await transpileFiles.rebuild();
437
435
  await bundleFiles.rebuild();
438
436
  const listener = await startListener(config, true);
439
437
  setTimeout(async () => {
@@ -446,7 +444,6 @@ async function startDevServer(config) {
446
444
  await Promise.allSettled([
447
445
  listener.close(),
448
446
  fileWatcher?.close(),
449
- transpileFiles.dispose(),
450
447
  bundleFiles.dispose()
451
448
  ]);
452
449
  });
@@ -467,14 +464,17 @@ async function startDevServer(config) {
467
464
  ignored: [buildDir, outDir],
468
465
  ignoreInitial: true
469
466
  });
470
- fileWatcher.on("all", async (_event, file) => {
467
+ fileWatcher.on("all", async (event, file) => {
468
+ if (event === "add" || event === "addDir") {
469
+ return;
470
+ }
471
471
  await createRoutesModule(config);
472
472
  let bundleCreated = false;
473
473
  try {
474
474
  logger.log(
475
475
  "Change detected. Bundling files and restarting server...."
476
476
  );
477
- await transpileFiles.rebuild();
477
+ await transpileFiles(config);
478
478
  await bundleFiles.rebuild();
479
479
  bundleCreated = true;
480
480
  } catch (err) {
@@ -528,6 +528,7 @@ for (const route of routes) {
528
528
  app.rpc({
529
529
  name: route.id,
530
530
  method: route.route.method,
531
+ path: route.route.path,
531
532
  params: route.route.params,
532
533
  response: route.route.response,
533
534
  handler: route.route.handler,
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const config = require('./shared/arri.406d936a.cjs');
3
+ const config = require('./shared/arri.ed4bd5e1.cjs');
4
4
  const h3 = require('h3');
5
5
  require('arri-validate');
6
6
  require('arri-codegen-utils');
@@ -17,15 +17,18 @@ exports.createRpcDefinition = config.createRpcDefinition;
17
17
  exports.defaultConfig = config.defaultConfig;
18
18
  exports.defineConfig = config.defineConfig;
19
19
  exports.defineError = config.defineError;
20
+ exports.defineRoute = config.defineRoute;
20
21
  exports.defineRpc = config.defineRpc;
21
22
  exports.errorResponseFromValidationErrors = config.errorResponseFromValidationErrors;
22
23
  exports.getRpcParamName = config.getRpcParamName;
23
24
  exports.getRpcPath = config.getRpcPath;
24
25
  exports.getRpcResponseName = config.getRpcResponseName;
25
26
  exports.handleH3Error = config.handleH3Error;
27
+ exports.handleRoute = config.handleRoute;
26
28
  exports.isArriConfig = config.isArriConfig;
27
29
  exports.isResolvedArriConfig = config.isResolvedArriConfig;
28
30
  exports.isRpc = config.isRpc;
31
+ exports.registerRoute = config.registerRoute;
29
32
  exports.registerRpc = config.registerRpc;
30
33
  exports.defineMiddleware = defineMiddleware;
31
34
  Object.keys(h3).forEach(function (k) {
package/dist/index.d.cts CHANGED
@@ -1,36 +1,54 @@
1
1
  import { HttpMethod, RpcDefinition, AppDefinition, ClientGenerator } from 'arri-codegen-utils';
2
2
  import * as arri_validate from 'arri-validate';
3
- import { AObjectSchema, InferType, a, ValidationError } from 'arri-validate';
4
- import { H3Event, H3EventContext, Router, App, H3Error } from 'h3';
3
+ import { AObjectSchema, ASchema, InferType, a, ValidationError } from 'arri-validate';
4
+ import { H3EventContext, H3Event, Router, App, H3Error } from 'h3';
5
5
  export * from 'h3';
6
6
  import { BuildOptions } from 'esbuild';
7
7
 
8
- type Middleware = (event: H3Event) => void | Promise<void>;
8
+ interface MiddlewareEventContext extends H3EventContext {
9
+ rpcName?: string;
10
+ }
11
+ interface MiddlewareEvent extends H3Event {
12
+ context: MiddlewareEventContext;
13
+ }
14
+ type Middleware = (event: MiddlewareEvent) => void | Promise<void>;
9
15
  declare const defineMiddleware: (middleware: Middleware) => Middleware;
10
- type ExtractParam$1<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
11
- type ExtractParams$1<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam$1<Segment, ExtractParams$1<Rest>> : ExtractParam$1<Path, {}>;
12
-
13
16
  type ExtractParam<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
14
17
  type ExtractParams<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam<Segment, ExtractParams<Rest>> : ExtractParam<Path, {}>;
15
- interface RouteEventContext<TPath extends string> extends H3EventContext {
18
+
19
+ interface RouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3EventContext {
16
20
  params: ExtractParams<TPath>;
21
+ query: TQuery;
22
+ body: TBody;
17
23
  }
18
- interface RouteEvent<TPath extends string> extends H3Event {
19
- context: RouteEventContext<TPath>;
24
+ interface RouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3Event {
25
+ context: RouteEventContext<TPath, TQuery, TBody>;
20
26
  }
21
- interface ArriRoute<TPath extends string> {
27
+ interface PostRouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends RouteEventContext<TPath, TQuery, TBody> {
28
+ response: TResponse;
29
+ }
30
+ interface PostRouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends H3Event {
31
+ context: PostRouteEventContext<TPath, TQuery, TBody, TResponse>;
32
+ }
33
+ interface ArriRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any> {
22
34
  path: TPath;
23
35
  method: HttpMethod | HttpMethod[];
24
- handler: (event: RouteEvent<TPath>) => any;
25
- postHandler?: (event: RouteEvent<TPath>) => any;
36
+ query?: TQuery;
37
+ body?: TBody;
38
+ handler: (event: RouteEvent<TPath, InferType<TQuery>, InferType<TBody>>) => TResponse;
39
+ postHandler?: (event: PostRouteEvent<TPath, InferType<TQuery>, InferType<TBody>, Awaited<TResponse>>) => any;
26
40
  }
41
+ declare function defineRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends AObjectSchema<any, any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): ArriRoute<TPath, TQuery, TBody, TResponse>;
27
42
  type RouteOptions = Pick<ArriOptions, "onAfterResponse" | "onBeforeResponse" | "onError" | "onRequest"> & {
28
43
  middleware: Middleware[];
29
44
  };
45
+ declare function registerRoute(router: Router, route: ArriRoute<any>, opts: RouteOptions): void;
46
+ declare function handleRoute(router: Router, method: HttpMethod, route: ArriRoute<any>, opts: RouteOptions): void;
30
47
 
31
48
  interface ArriProcedure<TParams extends AObjectSchema | undefined, TResponse extends AObjectSchema | undefined> {
32
49
  description?: string;
33
50
  method?: HttpMethod;
51
+ path?: string;
34
52
  params: TParams;
35
53
  response: TResponse;
36
54
  handler: ArriProcedureHandler<TParams extends AObjectSchema ? InferType<TParams> : undefined, TResponse extends AObjectSchema ? InferType<TResponse> : void>;
@@ -41,32 +59,39 @@ interface ArriNamedProcedure<TParams extends AObjectSchema | undefined, TRespons
41
59
  }
42
60
  type HandlerContext = Record<string, any>;
43
61
  interface RpcHandlerContext<TParams = undefined> extends HandlerContext, Omit<H3EventContext, "params"> {
62
+ rpcName: string;
44
63
  params: TParams;
45
64
  }
46
65
  interface RpcPostHandlerContext<TParams = undefined, TResponse = undefined> extends RpcHandlerContext<TParams> {
47
66
  response: TResponse;
48
67
  }
49
- type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: H3Event) => TResponse | Promise<TResponse>;
50
- type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: H3Event) => any;
68
+ interface RpcEvent<TParams = undefined> extends Omit<H3Event, "context"> {
69
+ context: RpcHandlerContext<TParams>;
70
+ }
71
+ interface RpcPostEvent<TParams = undefined, TResponse = undefined> extends Omit<H3Event, "context"> {
72
+ context: RpcPostHandlerContext<TParams, TResponse>;
73
+ }
74
+ type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: RpcEvent<TParams>) => TResponse | Promise<TResponse>;
75
+ type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: RpcPostEvent<TParams, TResponse>) => any;
51
76
  declare function isRpc(input: any): input is ArriProcedure<any, any>;
52
77
  declare function defineRpc<TParams extends AObjectSchema | undefined = undefined, TResponse extends AObjectSchema | undefined | never = undefined>(config: ArriProcedure<TParams, TResponse>): ArriProcedure<TParams, TResponse>;
53
78
  declare function createRpcDefinition(rpcName: string, httpPath: string, procedure: ArriProcedure<any, any>): RpcDefinition;
54
79
  declare function getRpcPath(rpcName: string, prefix?: string): string;
55
80
  declare function getRpcParamName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
56
81
  declare function getRpcResponseName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
57
- declare function registerRpc(router: Router, path: string, procedure: ArriProcedure<any, any>, opts: RouteOptions): void;
82
+ declare function registerRpc(router: Router, path: string, procedure: ArriNamedProcedure<any, any>, opts: RouteOptions): void;
58
83
 
59
84
  interface ArriRouterBase {
60
85
  rpc: <TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>) => void;
61
- route: <TPath extends string>(route: ArriRoute<TPath>) => void;
86
+ route: <TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>) => void;
62
87
  }
63
88
  declare class ArriRouter implements ArriRouterBase {
64
89
  private readonly procedures;
65
90
  private readonly routes;
66
91
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
67
- route<TPath extends string>(route: ArriRoute<TPath>): void;
92
+ route<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
68
93
  getProcedures(): ArriNamedProcedure<any, any>[];
69
- getRoutes(): ArriRoute<any>[];
94
+ getRoutes(): ArriRoute<any, any, any, any>[];
70
95
  }
71
96
 
72
97
  declare const DEV_ENDPOINT_ROOT = "/__arri_dev__";
@@ -88,7 +113,7 @@ declare class ArriApp implements ArriRouterBase {
88
113
  constructor(opts?: ArriOptions);
89
114
  use(input: Middleware | ArriRouter): void;
90
115
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
91
- route<TPath extends string>(route: ArriRoute<TPath>): void;
116
+ route<TPath extends string, TQuery extends AObjectSchema<any, any>, TBody extends ASchema<any>, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
92
117
  getAppDefinition(): AppDefinition;
93
118
  }
94
119
  interface ArriOptions {
@@ -103,10 +128,10 @@ interface ArriOptions {
103
128
  * This parameters also takes the rpcRoutePrefix option into account
104
129
  */
105
130
  rpcDefinitionPath?: string;
106
- onRequest?: (event: H3Event) => void | Promise<void>;
107
- onAfterResponse?: (event: H3Event) => void | Promise<void>;
108
- onBeforeResponse?: (event: H3Event) => void | Promise<void>;
109
- onError?: (error: H3Error, event: H3Event) => void | Promise<void>;
131
+ onRequest?: (event: MiddlewareEvent) => void | Promise<void>;
132
+ onAfterResponse?: (event: MiddlewareEvent) => void | Promise<void>;
133
+ onBeforeResponse?: (event: MiddlewareEvent) => void | Promise<void>;
134
+ onError?: (error: H3Error, event: MiddlewareEvent) => void | Promise<void>;
110
135
  }
111
136
 
112
137
  declare const ErrorResponse: arri_validate.AObjectSchema<{
@@ -143,4 +168,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
143
168
  declare const defaultConfig: Required<ArriConfig>;
144
169
  declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
145
170
 
146
- export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam$1 as ExtractParam, type ExtractParams$1 as ExtractParams, type HandlerContext, type Middleware, type ResolvedArriConfig, type RpcHandlerContext, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, isArriConfig, isResolvedArriConfig, isRpc, registerRpc };
171
+ export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam, type ExtractParams, type HandlerContext, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type RpcEvent, type RpcHandlerContext, type RpcPostEvent, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isResolvedArriConfig, isRpc, registerRoute, registerRpc };
package/dist/index.d.mts CHANGED
@@ -1,36 +1,54 @@
1
1
  import { HttpMethod, RpcDefinition, AppDefinition, ClientGenerator } from 'arri-codegen-utils';
2
2
  import * as arri_validate from 'arri-validate';
3
- import { AObjectSchema, InferType, a, ValidationError } from 'arri-validate';
4
- import { H3Event, H3EventContext, Router, App, H3Error } from 'h3';
3
+ import { AObjectSchema, ASchema, InferType, a, ValidationError } from 'arri-validate';
4
+ import { H3EventContext, H3Event, Router, App, H3Error } from 'h3';
5
5
  export * from 'h3';
6
6
  import { BuildOptions } from 'esbuild';
7
7
 
8
- type Middleware = (event: H3Event) => void | Promise<void>;
8
+ interface MiddlewareEventContext extends H3EventContext {
9
+ rpcName?: string;
10
+ }
11
+ interface MiddlewareEvent extends H3Event {
12
+ context: MiddlewareEventContext;
13
+ }
14
+ type Middleware = (event: MiddlewareEvent) => void | Promise<void>;
9
15
  declare const defineMiddleware: (middleware: Middleware) => Middleware;
10
- type ExtractParam$1<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
11
- type ExtractParams$1<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam$1<Segment, ExtractParams$1<Rest>> : ExtractParam$1<Path, {}>;
12
-
13
16
  type ExtractParam<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
14
17
  type ExtractParams<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam<Segment, ExtractParams<Rest>> : ExtractParam<Path, {}>;
15
- interface RouteEventContext<TPath extends string> extends H3EventContext {
18
+
19
+ interface RouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3EventContext {
16
20
  params: ExtractParams<TPath>;
21
+ query: TQuery;
22
+ body: TBody;
17
23
  }
18
- interface RouteEvent<TPath extends string> extends H3Event {
19
- context: RouteEventContext<TPath>;
24
+ interface RouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3Event {
25
+ context: RouteEventContext<TPath, TQuery, TBody>;
20
26
  }
21
- interface ArriRoute<TPath extends string> {
27
+ interface PostRouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends RouteEventContext<TPath, TQuery, TBody> {
28
+ response: TResponse;
29
+ }
30
+ interface PostRouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends H3Event {
31
+ context: PostRouteEventContext<TPath, TQuery, TBody, TResponse>;
32
+ }
33
+ interface ArriRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any> {
22
34
  path: TPath;
23
35
  method: HttpMethod | HttpMethod[];
24
- handler: (event: RouteEvent<TPath>) => any;
25
- postHandler?: (event: RouteEvent<TPath>) => any;
36
+ query?: TQuery;
37
+ body?: TBody;
38
+ handler: (event: RouteEvent<TPath, InferType<TQuery>, InferType<TBody>>) => TResponse;
39
+ postHandler?: (event: PostRouteEvent<TPath, InferType<TQuery>, InferType<TBody>, Awaited<TResponse>>) => any;
26
40
  }
41
+ declare function defineRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends AObjectSchema<any, any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): ArriRoute<TPath, TQuery, TBody, TResponse>;
27
42
  type RouteOptions = Pick<ArriOptions, "onAfterResponse" | "onBeforeResponse" | "onError" | "onRequest"> & {
28
43
  middleware: Middleware[];
29
44
  };
45
+ declare function registerRoute(router: Router, route: ArriRoute<any>, opts: RouteOptions): void;
46
+ declare function handleRoute(router: Router, method: HttpMethod, route: ArriRoute<any>, opts: RouteOptions): void;
30
47
 
31
48
  interface ArriProcedure<TParams extends AObjectSchema | undefined, TResponse extends AObjectSchema | undefined> {
32
49
  description?: string;
33
50
  method?: HttpMethod;
51
+ path?: string;
34
52
  params: TParams;
35
53
  response: TResponse;
36
54
  handler: ArriProcedureHandler<TParams extends AObjectSchema ? InferType<TParams> : undefined, TResponse extends AObjectSchema ? InferType<TResponse> : void>;
@@ -41,32 +59,39 @@ interface ArriNamedProcedure<TParams extends AObjectSchema | undefined, TRespons
41
59
  }
42
60
  type HandlerContext = Record<string, any>;
43
61
  interface RpcHandlerContext<TParams = undefined> extends HandlerContext, Omit<H3EventContext, "params"> {
62
+ rpcName: string;
44
63
  params: TParams;
45
64
  }
46
65
  interface RpcPostHandlerContext<TParams = undefined, TResponse = undefined> extends RpcHandlerContext<TParams> {
47
66
  response: TResponse;
48
67
  }
49
- type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: H3Event) => TResponse | Promise<TResponse>;
50
- type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: H3Event) => any;
68
+ interface RpcEvent<TParams = undefined> extends Omit<H3Event, "context"> {
69
+ context: RpcHandlerContext<TParams>;
70
+ }
71
+ interface RpcPostEvent<TParams = undefined, TResponse = undefined> extends Omit<H3Event, "context"> {
72
+ context: RpcPostHandlerContext<TParams, TResponse>;
73
+ }
74
+ type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: RpcEvent<TParams>) => TResponse | Promise<TResponse>;
75
+ type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: RpcPostEvent<TParams, TResponse>) => any;
51
76
  declare function isRpc(input: any): input is ArriProcedure<any, any>;
52
77
  declare function defineRpc<TParams extends AObjectSchema | undefined = undefined, TResponse extends AObjectSchema | undefined | never = undefined>(config: ArriProcedure<TParams, TResponse>): ArriProcedure<TParams, TResponse>;
53
78
  declare function createRpcDefinition(rpcName: string, httpPath: string, procedure: ArriProcedure<any, any>): RpcDefinition;
54
79
  declare function getRpcPath(rpcName: string, prefix?: string): string;
55
80
  declare function getRpcParamName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
56
81
  declare function getRpcResponseName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
57
- declare function registerRpc(router: Router, path: string, procedure: ArriProcedure<any, any>, opts: RouteOptions): void;
82
+ declare function registerRpc(router: Router, path: string, procedure: ArriNamedProcedure<any, any>, opts: RouteOptions): void;
58
83
 
59
84
  interface ArriRouterBase {
60
85
  rpc: <TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>) => void;
61
- route: <TPath extends string>(route: ArriRoute<TPath>) => void;
86
+ route: <TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>) => void;
62
87
  }
63
88
  declare class ArriRouter implements ArriRouterBase {
64
89
  private readonly procedures;
65
90
  private readonly routes;
66
91
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
67
- route<TPath extends string>(route: ArriRoute<TPath>): void;
92
+ route<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
68
93
  getProcedures(): ArriNamedProcedure<any, any>[];
69
- getRoutes(): ArriRoute<any>[];
94
+ getRoutes(): ArriRoute<any, any, any, any>[];
70
95
  }
71
96
 
72
97
  declare const DEV_ENDPOINT_ROOT = "/__arri_dev__";
@@ -88,7 +113,7 @@ declare class ArriApp implements ArriRouterBase {
88
113
  constructor(opts?: ArriOptions);
89
114
  use(input: Middleware | ArriRouter): void;
90
115
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
91
- route<TPath extends string>(route: ArriRoute<TPath>): void;
116
+ route<TPath extends string, TQuery extends AObjectSchema<any, any>, TBody extends ASchema<any>, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
92
117
  getAppDefinition(): AppDefinition;
93
118
  }
94
119
  interface ArriOptions {
@@ -103,10 +128,10 @@ interface ArriOptions {
103
128
  * This parameters also takes the rpcRoutePrefix option into account
104
129
  */
105
130
  rpcDefinitionPath?: string;
106
- onRequest?: (event: H3Event) => void | Promise<void>;
107
- onAfterResponse?: (event: H3Event) => void | Promise<void>;
108
- onBeforeResponse?: (event: H3Event) => void | Promise<void>;
109
- onError?: (error: H3Error, event: H3Event) => void | Promise<void>;
131
+ onRequest?: (event: MiddlewareEvent) => void | Promise<void>;
132
+ onAfterResponse?: (event: MiddlewareEvent) => void | Promise<void>;
133
+ onBeforeResponse?: (event: MiddlewareEvent) => void | Promise<void>;
134
+ onError?: (error: H3Error, event: MiddlewareEvent) => void | Promise<void>;
110
135
  }
111
136
 
112
137
  declare const ErrorResponse: arri_validate.AObjectSchema<{
@@ -143,4 +168,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
143
168
  declare const defaultConfig: Required<ArriConfig>;
144
169
  declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
145
170
 
146
- export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam$1 as ExtractParam, type ExtractParams$1 as ExtractParams, type HandlerContext, type Middleware, type ResolvedArriConfig, type RpcHandlerContext, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, isArriConfig, isResolvedArriConfig, isRpc, registerRpc };
171
+ export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam, type ExtractParams, type HandlerContext, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type RpcEvent, type RpcHandlerContext, type RpcPostEvent, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isResolvedArriConfig, isRpc, registerRoute, registerRpc };
package/dist/index.d.ts CHANGED
@@ -1,36 +1,54 @@
1
1
  import { HttpMethod, RpcDefinition, AppDefinition, ClientGenerator } from 'arri-codegen-utils';
2
2
  import * as arri_validate from 'arri-validate';
3
- import { AObjectSchema, InferType, a, ValidationError } from 'arri-validate';
4
- import { H3Event, H3EventContext, Router, App, H3Error } from 'h3';
3
+ import { AObjectSchema, ASchema, InferType, a, ValidationError } from 'arri-validate';
4
+ import { H3EventContext, H3Event, Router, App, H3Error } from 'h3';
5
5
  export * from 'h3';
6
6
  import { BuildOptions } from 'esbuild';
7
7
 
8
- type Middleware = (event: H3Event) => void | Promise<void>;
8
+ interface MiddlewareEventContext extends H3EventContext {
9
+ rpcName?: string;
10
+ }
11
+ interface MiddlewareEvent extends H3Event {
12
+ context: MiddlewareEventContext;
13
+ }
14
+ type Middleware = (event: MiddlewareEvent) => void | Promise<void>;
9
15
  declare const defineMiddleware: (middleware: Middleware) => Middleware;
10
- type ExtractParam$1<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
11
- type ExtractParams$1<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam$1<Segment, ExtractParams$1<Rest>> : ExtractParam$1<Path, {}>;
12
-
13
16
  type ExtractParam<Path, NextPart> = Path extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
14
17
  type ExtractParams<Path> = Path extends `${infer Segment}/${infer Rest}` ? ExtractParam<Segment, ExtractParams<Rest>> : ExtractParam<Path, {}>;
15
- interface RouteEventContext<TPath extends string> extends H3EventContext {
18
+
19
+ interface RouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3EventContext {
16
20
  params: ExtractParams<TPath>;
21
+ query: TQuery;
22
+ body: TBody;
17
23
  }
18
- interface RouteEvent<TPath extends string> extends H3Event {
19
- context: RouteEventContext<TPath>;
24
+ interface RouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any> extends H3Event {
25
+ context: RouteEventContext<TPath, TQuery, TBody>;
20
26
  }
21
- interface ArriRoute<TPath extends string> {
27
+ interface PostRouteEventContext<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends RouteEventContext<TPath, TQuery, TBody> {
28
+ response: TResponse;
29
+ }
30
+ interface PostRouteEvent<TPath extends string, TQuery extends Record<any, any> = any, TBody = any, TResponse = any> extends H3Event {
31
+ context: PostRouteEventContext<TPath, TQuery, TBody, TResponse>;
32
+ }
33
+ interface ArriRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any> {
22
34
  path: TPath;
23
35
  method: HttpMethod | HttpMethod[];
24
- handler: (event: RouteEvent<TPath>) => any;
25
- postHandler?: (event: RouteEvent<TPath>) => any;
36
+ query?: TQuery;
37
+ body?: TBody;
38
+ handler: (event: RouteEvent<TPath, InferType<TQuery>, InferType<TBody>>) => TResponse;
39
+ postHandler?: (event: PostRouteEvent<TPath, InferType<TQuery>, InferType<TBody>, Awaited<TResponse>>) => any;
26
40
  }
41
+ declare function defineRoute<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends AObjectSchema<any, any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): ArriRoute<TPath, TQuery, TBody, TResponse>;
27
42
  type RouteOptions = Pick<ArriOptions, "onAfterResponse" | "onBeforeResponse" | "onError" | "onRequest"> & {
28
43
  middleware: Middleware[];
29
44
  };
45
+ declare function registerRoute(router: Router, route: ArriRoute<any>, opts: RouteOptions): void;
46
+ declare function handleRoute(router: Router, method: HttpMethod, route: ArriRoute<any>, opts: RouteOptions): void;
30
47
 
31
48
  interface ArriProcedure<TParams extends AObjectSchema | undefined, TResponse extends AObjectSchema | undefined> {
32
49
  description?: string;
33
50
  method?: HttpMethod;
51
+ path?: string;
34
52
  params: TParams;
35
53
  response: TResponse;
36
54
  handler: ArriProcedureHandler<TParams extends AObjectSchema ? InferType<TParams> : undefined, TResponse extends AObjectSchema ? InferType<TResponse> : void>;
@@ -41,32 +59,39 @@ interface ArriNamedProcedure<TParams extends AObjectSchema | undefined, TRespons
41
59
  }
42
60
  type HandlerContext = Record<string, any>;
43
61
  interface RpcHandlerContext<TParams = undefined> extends HandlerContext, Omit<H3EventContext, "params"> {
62
+ rpcName: string;
44
63
  params: TParams;
45
64
  }
46
65
  interface RpcPostHandlerContext<TParams = undefined, TResponse = undefined> extends RpcHandlerContext<TParams> {
47
66
  response: TResponse;
48
67
  }
49
- type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: H3Event) => TResponse | Promise<TResponse>;
50
- type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: H3Event) => any;
68
+ interface RpcEvent<TParams = undefined> extends Omit<H3Event, "context"> {
69
+ context: RpcHandlerContext<TParams>;
70
+ }
71
+ interface RpcPostEvent<TParams = undefined, TResponse = undefined> extends Omit<H3Event, "context"> {
72
+ context: RpcPostHandlerContext<TParams, TResponse>;
73
+ }
74
+ type ArriProcedureHandler<TParams, TResponse> = (context: RpcHandlerContext<TParams>, event: RpcEvent<TParams>) => TResponse | Promise<TResponse>;
75
+ type ArriProcedurePostHandler<TParams, TResponse> = (context: RpcPostHandlerContext<TParams, TResponse>, event: RpcPostEvent<TParams, TResponse>) => any;
51
76
  declare function isRpc(input: any): input is ArriProcedure<any, any>;
52
77
  declare function defineRpc<TParams extends AObjectSchema | undefined = undefined, TResponse extends AObjectSchema | undefined | never = undefined>(config: ArriProcedure<TParams, TResponse>): ArriProcedure<TParams, TResponse>;
53
78
  declare function createRpcDefinition(rpcName: string, httpPath: string, procedure: ArriProcedure<any, any>): RpcDefinition;
54
79
  declare function getRpcPath(rpcName: string, prefix?: string): string;
55
80
  declare function getRpcParamName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
56
81
  declare function getRpcResponseName(rpcName: string, procedure: ArriProcedure<any, any>): string | undefined;
57
- declare function registerRpc(router: Router, path: string, procedure: ArriProcedure<any, any>, opts: RouteOptions): void;
82
+ declare function registerRpc(router: Router, path: string, procedure: ArriNamedProcedure<any, any>, opts: RouteOptions): void;
58
83
 
59
84
  interface ArriRouterBase {
60
85
  rpc: <TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>) => void;
61
- route: <TPath extends string>(route: ArriRoute<TPath>) => void;
86
+ route: <TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>) => void;
62
87
  }
63
88
  declare class ArriRouter implements ArriRouterBase {
64
89
  private readonly procedures;
65
90
  private readonly routes;
66
91
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
67
- route<TPath extends string>(route: ArriRoute<TPath>): void;
92
+ route<TPath extends string, TQuery extends AObjectSchema<any, any> = any, TBody extends ASchema<any> = any, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
68
93
  getProcedures(): ArriNamedProcedure<any, any>[];
69
- getRoutes(): ArriRoute<any>[];
94
+ getRoutes(): ArriRoute<any, any, any, any>[];
70
95
  }
71
96
 
72
97
  declare const DEV_ENDPOINT_ROOT = "/__arri_dev__";
@@ -88,7 +113,7 @@ declare class ArriApp implements ArriRouterBase {
88
113
  constructor(opts?: ArriOptions);
89
114
  use(input: Middleware | ArriRouter): void;
90
115
  rpc<TParams extends AObjectSchema<any, any> | undefined, TResponse extends AObjectSchema<any, any> | undefined>(procedure: ArriNamedProcedure<TParams, TResponse>): void;
91
- route<TPath extends string>(route: ArriRoute<TPath>): void;
116
+ route<TPath extends string, TQuery extends AObjectSchema<any, any>, TBody extends ASchema<any>, TResponse = any>(route: ArriRoute<TPath, TQuery, TBody, TResponse>): void;
92
117
  getAppDefinition(): AppDefinition;
93
118
  }
94
119
  interface ArriOptions {
@@ -103,10 +128,10 @@ interface ArriOptions {
103
128
  * This parameters also takes the rpcRoutePrefix option into account
104
129
  */
105
130
  rpcDefinitionPath?: string;
106
- onRequest?: (event: H3Event) => void | Promise<void>;
107
- onAfterResponse?: (event: H3Event) => void | Promise<void>;
108
- onBeforeResponse?: (event: H3Event) => void | Promise<void>;
109
- onError?: (error: H3Error, event: H3Event) => void | Promise<void>;
131
+ onRequest?: (event: MiddlewareEvent) => void | Promise<void>;
132
+ onAfterResponse?: (event: MiddlewareEvent) => void | Promise<void>;
133
+ onBeforeResponse?: (event: MiddlewareEvent) => void | Promise<void>;
134
+ onError?: (error: H3Error, event: MiddlewareEvent) => void | Promise<void>;
110
135
  }
111
136
 
112
137
  declare const ErrorResponse: arri_validate.AObjectSchema<{
@@ -143,4 +168,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
143
168
  declare const defaultConfig: Required<ArriConfig>;
144
169
  declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
145
170
 
146
- export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam$1 as ExtractParam, type ExtractParams$1 as ExtractParams, type HandlerContext, type Middleware, type ResolvedArriConfig, type RpcHandlerContext, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, isArriConfig, isResolvedArriConfig, isRpc, registerRpc };
171
+ export { ArriApp, type ArriConfig, type ArriNamedProcedure, type ArriOptions, type ArriProcedure, type ArriProcedureHandler, type ArriProcedurePostHandler, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, type ExtractParam, type ExtractParams, type HandlerContext, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type RpcEvent, type RpcHandlerContext, type RpcPostEvent, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isResolvedArriConfig, isRpc, registerRoute, registerRpc };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ArriApp, o as ArriRouter, a as DEV_DEFINITION_ENDPOINT, D as DEV_ENDPOINT_ROOT, E as ErrorResponse, c as createRpcDefinition, m as defaultConfig, n as defineConfig, f as defineError, d as defineRpc, h as errorResponseFromValidationErrors, b as getRpcParamName, g as getRpcPath, e as getRpcResponseName, j as handleH3Error, k as isArriConfig, l as isResolvedArriConfig, i as isRpc, r as registerRpc } from './shared/arri.8539c7bd.mjs';
1
+ export { A as ArriApp, o as ArriRouter, a as DEV_DEFINITION_ENDPOINT, D as DEV_ENDPOINT_ROOT, E as ErrorResponse, c as createRpcDefinition, m as defaultConfig, n as defineConfig, f as defineError, p as defineRoute, d as defineRpc, h as errorResponseFromValidationErrors, b as getRpcParamName, g as getRpcPath, e as getRpcResponseName, j as handleH3Error, s as handleRoute, k as isArriConfig, l as isResolvedArriConfig, i as isRpc, q as registerRoute, r as registerRpc } from './shared/arri.121d68e9.mjs';
2
2
  export * from 'h3';
3
3
  import 'arri-validate';
4
4
  import 'arri-codegen-utils';
@@ -1,5 +1,5 @@
1
1
  import { a, isAObjectSchema } from 'arri-validate';
2
- import { createError, isError, setResponseStatus, send, eventHandler, isPreflightRequest, readRawBody, getValidatedQuery, setResponseHeader, defineEventHandler, setHeader, createRouter, createApp, sendError } from 'h3';
2
+ import { createError, isError, setResponseStatus, send, eventHandler, isPreflightRequest, readRawBody, getValidatedQuery, setResponseHeader, defineEventHandler, getQuery, setHeader, createRouter, createApp, sendError } from 'h3';
3
3
  import { isHttpMethod, removeDisallowedChars } from 'arri-codegen-utils';
4
4
  import { kebabCase, pascalCase } from 'scule';
5
5
 
@@ -270,10 +270,15 @@ function getRpcResponseDefinition(rpcName, procedure) {
270
270
  return name;
271
271
  }
272
272
  function registerRpc(router, path, procedure, opts) {
273
- const paramValidator = procedure.params ? a.compile(procedure.params) : void 0;
274
- const responseValidator = procedure.response ? a.compile(procedure.response) : void 0;
273
+ let responseValidator;
274
+ try {
275
+ responseValidator = procedure.response ? a.compile(procedure.response) : void 0;
276
+ } catch (err) {
277
+ console.error("ERROR COMPILING VALIDATOR", err);
278
+ }
275
279
  const httpMethod = procedure.method ?? "post";
276
280
  const handler = eventHandler(async (event) => {
281
+ event.context.rpcName = procedure.name;
277
282
  if (isPreflightRequest(event)) {
278
283
  return "ok";
279
284
  }
@@ -286,7 +291,7 @@ function registerRpc(router, path, procedure, opts) {
286
291
  await m(event);
287
292
  }
288
293
  }
289
- if (isAObjectSchema(procedure.params) && paramValidator) {
294
+ if (isAObjectSchema(procedure.params)) {
290
295
  switch (httpMethod) {
291
296
  case "get":
292
297
  case "head": {
@@ -326,7 +331,10 @@ function registerRpc(router, path, procedure, opts) {
326
331
  statusMessage: `Invalid request body. Expected object. Got undefined.`
327
332
  });
328
333
  }
329
- const parsedParams = paramValidator.safeParse(body);
334
+ const parsedParams = a.safeParse(
335
+ procedure.params,
336
+ body
337
+ );
330
338
  if (!parsedParams.success) {
331
339
  const errorParts = [];
332
340
  for (const err of parsedParams.error.errors) {
@@ -365,21 +373,8 @@ function registerRpc(router, path, procedure, opts) {
365
373
  responseValidator?.serialize(response) ?? JSON.stringify(response)
366
374
  );
367
375
  } else {
368
- await send(
369
- event,
370
- response ?? `<!doctype html>
371
- <html lang="en">
372
- <head>
373
- <meta charset="UTF-8" />
374
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
375
- <title>Empty Response</title>
376
- </head>
377
- <body>
378
- nothing to see here
379
- </body>
380
- </html>
381
- `
382
- );
376
+ setResponseHeader(event, "Content-Type", "application/json");
377
+ await send(event, `{}`);
383
378
  }
384
379
  if (opts.onAfterResponse) {
385
380
  await opts.onAfterResponse(event);
@@ -440,6 +435,9 @@ class ArriRouter {
440
435
  }
441
436
  }
442
437
 
438
+ function defineRoute(route) {
439
+ return route;
440
+ }
443
441
  function registerRoute(router, route, opts) {
444
442
  if (typeof route.method === "string") {
445
443
  handleRoute(router, route.method, route, opts);
@@ -451,6 +449,9 @@ function registerRoute(router, route, opts) {
451
449
  }
452
450
  function handleRoute(router, method, route, opts) {
453
451
  const handler = defineEventHandler(async (event) => {
452
+ if (isPreflightRequest(event)) {
453
+ return "ok";
454
+ }
454
455
  try {
455
456
  if (opts.onRequest) {
456
457
  await opts.onRequest(event);
@@ -460,6 +461,55 @@ function handleRoute(router, method, route, opts) {
460
461
  await m(event);
461
462
  }
462
463
  }
464
+ if (route.query) {
465
+ const query = getQuery(event);
466
+ const parsedQuery = a.safeCoerce(route.query, query);
467
+ if (!parsedQuery.success) {
468
+ const errParts = [];
469
+ for (const err of parsedQuery.error.errors) {
470
+ const errPath = err.instancePath.split("/");
471
+ errPath.shift();
472
+ const propName = errPath.join(".");
473
+ if (!errParts.includes(propName)) {
474
+ errParts.push(propName);
475
+ }
476
+ }
477
+ const message = `Missing or invalid url query parameters: [${errParts.join(
478
+ ", "
479
+ )}]`;
480
+ throw defineError(400, {
481
+ statusMessage: message
482
+ });
483
+ }
484
+ event.context.query = parsedQuery.value;
485
+ }
486
+ const notAllowedBodyMethods = [
487
+ "GET",
488
+ "HEAD",
489
+ "CONNECT",
490
+ "OPTIONS"
491
+ ];
492
+ if (route.body && !notAllowedBodyMethods.includes(event.method)) {
493
+ const body = await readRawBody(event);
494
+ const parsedBody = a.safeParse(route.body, body);
495
+ if (!parsedBody.success) {
496
+ const errorParts = [];
497
+ for (const err of parsedBody.error.errors) {
498
+ const errPath = err.instancePath.split("/");
499
+ errPath.shift();
500
+ if (!errorParts.includes(errPath.join("."))) {
501
+ errorParts.push(errPath.join("."));
502
+ }
503
+ }
504
+ throw defineError(400, {
505
+ statusMessage: `Invalid request body. Affected properties [${errorParts.join(
506
+ ", "
507
+ )}]`,
508
+ data: parsedBody.error
509
+ });
510
+ }
511
+ event.context.body = parsedBody.value;
512
+ }
463
513
  const response = await route.handler(event);
464
514
  event.context.response = response;
465
515
  if (!event.handled) {
@@ -482,6 +532,7 @@ function handleRoute(router, method, route, opts) {
482
532
  } catch (err) {
483
533
  await handleH3Error(err, event, opts.onError);
484
534
  }
535
+ return "";
485
536
  });
486
537
  switch (method) {
487
538
  case "get":
@@ -532,6 +583,7 @@ class ArriApp {
532
583
  this.h3App = createApp({
533
584
  debug: opts?.debug
534
585
  });
586
+ this.onRequest = opts.onRequest;
535
587
  this.onError = opts.onError;
536
588
  this.onAfterResponse = opts.onAfterResponse;
537
589
  this.onBeforeResponse = opts.onBeforeResponse;
@@ -554,6 +606,9 @@ class ArriApp {
554
606
  setResponseStatus(event, 404);
555
607
  const error = defineError(404);
556
608
  try {
609
+ if (this.onRequest) {
610
+ await this.onRequest(event);
611
+ }
557
612
  if (this.middlewares.length) {
558
613
  for (const m of this.middlewares) {
559
614
  await m(event);
@@ -588,7 +643,7 @@ class ArriApp {
588
643
  this.middlewares.push(input);
589
644
  }
590
645
  rpc(procedure) {
591
- const path = getRpcPath(procedure.name, this.rpcRoutePrefix);
646
+ const path = procedure.path ?? getRpcPath(procedure.name, this.rpcRoutePrefix);
592
647
  this.procedures[procedure.name] = createRpcDefinition(
593
648
  procedure.name,
594
649
  path,
@@ -608,6 +663,7 @@ class ArriApp {
608
663
  }
609
664
  registerRpc(this.h3Router, path, procedure, {
610
665
  middleware: this.middlewares,
666
+ onRequest: this.onRequest,
611
667
  onError: this.onError,
612
668
  onAfterResponse: this.onAfterResponse,
613
669
  onBeforeResponse: this.onBeforeResponse
@@ -616,6 +672,7 @@ class ArriApp {
616
672
  route(route) {
617
673
  registerRoute(this.h3Router, route, {
618
674
  middleware: this.middlewares,
675
+ onRequest: this.onRequest,
619
676
  onError: this.onError,
620
677
  onAfterResponse: this.onAfterResponse,
621
678
  onBeforeResponse: this.onBeforeResponse
@@ -626,8 +683,7 @@ class ArriApp {
626
683
  arriSchemaVersion: "0.0.2",
627
684
  info: this.appInfo,
628
685
  procedures: {},
629
- models: this.models,
630
- errors: ErrorResponse
686
+ models: this.models
631
687
  };
632
688
  for (const key of Object.keys(this.procedures)) {
633
689
  const rpc = this.procedures[key];
@@ -674,4 +730,4 @@ function defineConfig(config) {
674
730
  };
675
731
  }
676
732
 
677
- export { ArriApp as A, DEV_ENDPOINT_ROOT as D, ErrorResponse as E, DEV_DEFINITION_ENDPOINT as a, getRpcParamName as b, createRpcDefinition as c, defineRpc as d, getRpcResponseName as e, defineError as f, getRpcPath as g, errorResponseFromValidationErrors as h, isRpc as i, handleH3Error as j, isArriConfig as k, isResolvedArriConfig as l, defaultConfig as m, defineConfig as n, ArriRouter as o, registerRpc as r };
733
+ export { ArriApp as A, DEV_ENDPOINT_ROOT as D, ErrorResponse as E, DEV_DEFINITION_ENDPOINT as a, getRpcParamName as b, createRpcDefinition as c, defineRpc as d, getRpcResponseName as e, defineError as f, getRpcPath as g, errorResponseFromValidationErrors as h, isRpc as i, handleH3Error as j, isArriConfig as k, isResolvedArriConfig as l, defaultConfig as m, defineConfig as n, ArriRouter as o, defineRoute as p, registerRoute as q, registerRpc as r, handleRoute as s };
@@ -272,10 +272,15 @@ function getRpcResponseDefinition(rpcName, procedure) {
272
272
  return name;
273
273
  }
274
274
  function registerRpc(router, path, procedure, opts) {
275
- const paramValidator = procedure.params ? arriValidate.a.compile(procedure.params) : void 0;
276
- const responseValidator = procedure.response ? arriValidate.a.compile(procedure.response) : void 0;
275
+ let responseValidator;
276
+ try {
277
+ responseValidator = procedure.response ? arriValidate.a.compile(procedure.response) : void 0;
278
+ } catch (err) {
279
+ console.error("ERROR COMPILING VALIDATOR", err);
280
+ }
277
281
  const httpMethod = procedure.method ?? "post";
278
282
  const handler = h3.eventHandler(async (event) => {
283
+ event.context.rpcName = procedure.name;
279
284
  if (h3.isPreflightRequest(event)) {
280
285
  return "ok";
281
286
  }
@@ -288,7 +293,7 @@ function registerRpc(router, path, procedure, opts) {
288
293
  await m(event);
289
294
  }
290
295
  }
291
- if (arriValidate.isAObjectSchema(procedure.params) && paramValidator) {
296
+ if (arriValidate.isAObjectSchema(procedure.params)) {
292
297
  switch (httpMethod) {
293
298
  case "get":
294
299
  case "head": {
@@ -328,7 +333,10 @@ function registerRpc(router, path, procedure, opts) {
328
333
  statusMessage: `Invalid request body. Expected object. Got undefined.`
329
334
  });
330
335
  }
331
- const parsedParams = paramValidator.safeParse(body);
336
+ const parsedParams = arriValidate.a.safeParse(
337
+ procedure.params,
338
+ body
339
+ );
332
340
  if (!parsedParams.success) {
333
341
  const errorParts = [];
334
342
  for (const err of parsedParams.error.errors) {
@@ -367,21 +375,8 @@ function registerRpc(router, path, procedure, opts) {
367
375
  responseValidator?.serialize(response) ?? JSON.stringify(response)
368
376
  );
369
377
  } else {
370
- await h3.send(
371
- event,
372
- response ?? `<!doctype html>
373
- <html lang="en">
374
- <head>
375
- <meta charset="UTF-8" />
376
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
377
- <title>Empty Response</title>
378
- </head>
379
- <body>
380
- nothing to see here
381
- </body>
382
- </html>
383
- `
384
- );
378
+ h3.setResponseHeader(event, "Content-Type", "application/json");
379
+ await h3.send(event, `{}`);
385
380
  }
386
381
  if (opts.onAfterResponse) {
387
382
  await opts.onAfterResponse(event);
@@ -442,6 +437,9 @@ class ArriRouter {
442
437
  }
443
438
  }
444
439
 
440
+ function defineRoute(route) {
441
+ return route;
442
+ }
445
443
  function registerRoute(router, route, opts) {
446
444
  if (typeof route.method === "string") {
447
445
  handleRoute(router, route.method, route, opts);
@@ -453,6 +451,9 @@ function registerRoute(router, route, opts) {
453
451
  }
454
452
  function handleRoute(router, method, route, opts) {
455
453
  const handler = h3.defineEventHandler(async (event) => {
454
+ if (h3.isPreflightRequest(event)) {
455
+ return "ok";
456
+ }
456
457
  try {
457
458
  if (opts.onRequest) {
458
459
  await opts.onRequest(event);
@@ -462,6 +463,55 @@ function handleRoute(router, method, route, opts) {
462
463
  await m(event);
463
464
  }
464
465
  }
466
+ if (route.query) {
467
+ const query = h3.getQuery(event);
468
+ const parsedQuery = arriValidate.a.safeCoerce(route.query, query);
469
+ if (!parsedQuery.success) {
470
+ const errParts = [];
471
+ for (const err of parsedQuery.error.errors) {
472
+ const errPath = err.instancePath.split("/");
473
+ errPath.shift();
474
+ const propName = errPath.join(".");
475
+ if (!errParts.includes(propName)) {
476
+ errParts.push(propName);
477
+ }
478
+ }
479
+ const message = `Missing or invalid url query parameters: [${errParts.join(
480
+ ", "
481
+ )}]`;
482
+ throw defineError(400, {
483
+ statusMessage: message
484
+ });
485
+ }
486
+ event.context.query = parsedQuery.value;
487
+ }
488
+ const notAllowedBodyMethods = [
489
+ "GET",
490
+ "HEAD",
491
+ "CONNECT",
492
+ "OPTIONS"
493
+ ];
494
+ if (route.body && !notAllowedBodyMethods.includes(event.method)) {
495
+ const body = await h3.readRawBody(event);
496
+ const parsedBody = arriValidate.a.safeParse(route.body, body);
497
+ if (!parsedBody.success) {
498
+ const errorParts = [];
499
+ for (const err of parsedBody.error.errors) {
500
+ const errPath = err.instancePath.split("/");
501
+ errPath.shift();
502
+ if (!errorParts.includes(errPath.join("."))) {
503
+ errorParts.push(errPath.join("."));
504
+ }
505
+ }
506
+ throw defineError(400, {
507
+ statusMessage: `Invalid request body. Affected properties [${errorParts.join(
508
+ ", "
509
+ )}]`,
510
+ data: parsedBody.error
511
+ });
512
+ }
513
+ event.context.body = parsedBody.value;
514
+ }
465
515
  const response = await route.handler(event);
466
516
  event.context.response = response;
467
517
  if (!event.handled) {
@@ -484,6 +534,7 @@ function handleRoute(router, method, route, opts) {
484
534
  } catch (err) {
485
535
  await handleH3Error(err, event, opts.onError);
486
536
  }
537
+ return "";
487
538
  });
488
539
  switch (method) {
489
540
  case "get":
@@ -534,6 +585,7 @@ class ArriApp {
534
585
  this.h3App = h3.createApp({
535
586
  debug: opts?.debug
536
587
  });
588
+ this.onRequest = opts.onRequest;
537
589
  this.onError = opts.onError;
538
590
  this.onAfterResponse = opts.onAfterResponse;
539
591
  this.onBeforeResponse = opts.onBeforeResponse;
@@ -556,6 +608,9 @@ class ArriApp {
556
608
  h3.setResponseStatus(event, 404);
557
609
  const error = defineError(404);
558
610
  try {
611
+ if (this.onRequest) {
612
+ await this.onRequest(event);
613
+ }
559
614
  if (this.middlewares.length) {
560
615
  for (const m of this.middlewares) {
561
616
  await m(event);
@@ -590,7 +645,7 @@ class ArriApp {
590
645
  this.middlewares.push(input);
591
646
  }
592
647
  rpc(procedure) {
593
- const path = getRpcPath(procedure.name, this.rpcRoutePrefix);
648
+ const path = procedure.path ?? getRpcPath(procedure.name, this.rpcRoutePrefix);
594
649
  this.procedures[procedure.name] = createRpcDefinition(
595
650
  procedure.name,
596
651
  path,
@@ -610,6 +665,7 @@ class ArriApp {
610
665
  }
611
666
  registerRpc(this.h3Router, path, procedure, {
612
667
  middleware: this.middlewares,
668
+ onRequest: this.onRequest,
613
669
  onError: this.onError,
614
670
  onAfterResponse: this.onAfterResponse,
615
671
  onBeforeResponse: this.onBeforeResponse
@@ -618,6 +674,7 @@ class ArriApp {
618
674
  route(route) {
619
675
  registerRoute(this.h3Router, route, {
620
676
  middleware: this.middlewares,
677
+ onRequest: this.onRequest,
621
678
  onError: this.onError,
622
679
  onAfterResponse: this.onAfterResponse,
623
680
  onBeforeResponse: this.onBeforeResponse
@@ -628,8 +685,7 @@ class ArriApp {
628
685
  arriSchemaVersion: "0.0.2",
629
686
  info: this.appInfo,
630
687
  procedures: {},
631
- models: this.models,
632
- errors: ErrorResponse
688
+ models: this.models
633
689
  };
634
690
  for (const key of Object.keys(this.procedures)) {
635
691
  const rpc = this.procedures[key];
@@ -685,13 +741,16 @@ exports.createRpcDefinition = createRpcDefinition;
685
741
  exports.defaultConfig = defaultConfig;
686
742
  exports.defineConfig = defineConfig;
687
743
  exports.defineError = defineError;
744
+ exports.defineRoute = defineRoute;
688
745
  exports.defineRpc = defineRpc;
689
746
  exports.errorResponseFromValidationErrors = errorResponseFromValidationErrors;
690
747
  exports.getRpcParamName = getRpcParamName;
691
748
  exports.getRpcPath = getRpcPath;
692
749
  exports.getRpcResponseName = getRpcResponseName;
693
750
  exports.handleH3Error = handleH3Error;
751
+ exports.handleRoute = handleRoute;
694
752
  exports.isArriConfig = isArriConfig;
695
753
  exports.isResolvedArriConfig = isResolvedArriConfig;
696
754
  exports.isRpc = isRpc;
755
+ exports.registerRoute = registerRoute;
697
756
  exports.registerRpc = registerRpc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arri",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.10",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -22,7 +22,7 @@
22
22
  "chokidar": "^3.5.3",
23
23
  "citty": "^0.1.4",
24
24
  "consola": "^3.2.3",
25
- "esbuild": "^0.19.3",
25
+ "esbuild": "^0.19.4",
26
26
  "globby": "^13.2.2",
27
27
  "h3": "^1.8.2",
28
28
  "jiti": "^1.20.0",
@@ -31,12 +31,12 @@
31
31
  "pathe": "^1.1.1",
32
32
  "prettier": "^3.0.3",
33
33
  "scule": "^1.0.0",
34
- "arri-codegen-dart": "0.1.0-beta.0",
35
- "arri-codegen-ts": "0.1.0-beta.0",
36
- "arri-validate": "0.1.0-beta.0",
37
- "arri-codegen-utils": "0.1.0-beta.0"
34
+ "arri-validate": "0.1.0-beta.10",
35
+ "arri-codegen-ts": "0.1.0-beta.10",
36
+ "arri-codegen-dart": "0.1.0-beta.10",
37
+ "arri-codegen-utils": "0.1.0-beta.10"
38
38
  },
39
39
  "devDependencies": {
40
- "bun-types": "^1.0.3"
40
+ "bun-types": "^1.0.5"
41
41
  }
42
42
  }