@stone-js/use-react 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { InitializationError, isEmpty, isNotEmpty, isMetaClassModule, isMetaFactoryModule, isObjectLikeModule, isFunction, isFunctionModule, mergeBlueprints, stoneBlueprint, classDecoratorLegacyWrapper, setMetadata, methodDecoratorLegacyWrapper, addMetadata, LIFECYCLE_HOOK_KEY, hasMetadata, getMetadata, isMatchedAdapter, Logger, addBlueprint } from '@stone-js/core';
1
+ import { isNotEmpty, isEmpty, InitializationError, isMetaClassModule, isMetaFactoryModule, isObjectLikeModule, isFunction, isFunctionModule, classDecoratorLegacyWrapper, setMetadata, methodDecoratorLegacyWrapper, addMetadata, LIFECYCLE_HOOK_KEY, hasMetadata, getMetadata, isMatchedAdapter, mergeBlueprints, stoneBlueprint, Logger, addBlueprint } from '@stone-js/core';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { renderToString } from 'react-dom/server';
4
4
  import { createContext, StrictMode, useContext, useMemo, useState, useEffect } from 'react';
@@ -7,58 +7,6 @@ import { Config } from '@stone-js/config';
7
7
  import { GET, NODE_CONSOLE_PLATFORM, Router, RouteEvent } from '@stone-js/router';
8
8
  import { RedirectResponse, OutgoingHttpResponse, MetaCompressionMiddleware, MetaStaticFileMiddleware } from '@stone-js/http-core';
9
9
 
10
- /**
11
- * Custom error for react operations.
12
- */
13
- class UseReactError extends InitializationError {
14
- constructor(message, options) {
15
- super(message, options);
16
- this.name = 'UseReactError';
17
- }
18
- }
19
-
20
- /**
21
- * A useReact event handler for processing incoming events
22
- * For single event handler.
23
- *
24
- * Multiple event handlers will be processed by the router.
25
- *
26
- * @template IncomingEventType - The type representing the incoming event.
27
- * @template OutgoingResponseType - The type representing the outgoing response.
28
- */
29
- class UseReactEventHandler {
30
- blueprint;
31
- /**
32
- * Constructs a `UseReactEventHandler` instance.
33
- *
34
- * @param options - The UseReactEventHandler options including blueprint.
35
- */
36
- constructor({ blueprint }) {
37
- this.blueprint = blueprint;
38
- }
39
- /**
40
- * Handle an incoming event.
41
- *
42
- * @returns The outgoing response.
43
- */
44
- handle() {
45
- return this.getComponentEventHandler();
46
- }
47
- /**
48
- * Get the component event handler.
49
- *
50
- * @returns The component event handler.
51
- * @throws {UseReactError} If the component event handler is missing.
52
- */
53
- getComponentEventHandler() {
54
- const handler = this.blueprint.get('stone.useReact.componentEventHandler');
55
- if (isEmpty(handler)) {
56
- throw new UseReactError('The component event handler is missing.');
57
- }
58
- return handler;
59
- }
60
- }
61
-
62
10
  /**
63
11
  * Stone DOM Attribute.
64
12
  */
@@ -321,6 +269,16 @@ const StoneError = () => {
321
269
  return (jsxs(Fragment, { children: [jsx("h1", { children: "An error occured" }), jsx("p", { children: "Sorry, something went wrong." })] }));
322
270
  };
323
271
 
272
+ /**
273
+ * Custom error for react operations.
274
+ */
275
+ class UseReactError extends InitializationError {
276
+ constructor(message, options) {
277
+ super(message, options);
278
+ this.name = 'UseReactError';
279
+ }
280
+ }
281
+
324
282
  /**
325
283
  * Build the React application for the current route.
326
284
  * Or for the main handler if the route is not defined.
@@ -752,6 +710,48 @@ class ReactRuntime {
752
710
  */
753
711
  const MetaReactRuntime = { module: ReactRuntime, isClass: true, alias: 'reactRuntime', singleton: true };
754
712
 
713
+ /**
714
+ * A useReact event handler for processing incoming events
715
+ * For single event handler.
716
+ *
717
+ * Multiple event handlers will be processed by the router.
718
+ *
719
+ * @template IncomingEventType - The type representing the incoming event.
720
+ * @template OutgoingResponseType - The type representing the outgoing response.
721
+ */
722
+ class UseReactEventHandler {
723
+ blueprint;
724
+ /**
725
+ * Constructs a `UseReactEventHandler` instance.
726
+ *
727
+ * @param options - The UseReactEventHandler options including blueprint.
728
+ */
729
+ constructor({ blueprint }) {
730
+ this.blueprint = blueprint;
731
+ }
732
+ /**
733
+ * Handle an incoming event.
734
+ *
735
+ * @returns The outgoing response.
736
+ */
737
+ handle() {
738
+ return this.getComponentEventHandler();
739
+ }
740
+ /**
741
+ * Get the component event handler.
742
+ *
743
+ * @returns The component event handler.
744
+ * @throws {UseReactError} If the component event handler is missing.
745
+ */
746
+ getComponentEventHandler() {
747
+ const handler = this.blueprint.get('stone.useReact.componentEventHandler');
748
+ if (isEmpty(handler)) {
749
+ throw new UseReactError('The component event handler is missing.');
750
+ }
751
+ return handler;
752
+ }
753
+ }
754
+
755
755
  /**
756
756
  * Class representing an UseReactUseReactKernelErrorHandler.
757
757
  *
@@ -913,230 +913,93 @@ class UseReactServiceProvider {
913
913
  const MetaUseReactServiceProvider = { module: UseReactServiceProvider, isClass: true };
914
914
 
915
915
  /**
916
- * Utility function to define an adapter error page.
917
- *
918
- * @param module - The adapter error page module.
919
- * @param options - Optional adapter error page options.
920
- * @returns The UseReactBlueprint.
916
+ * Constants are defined here to prevent Circular dependency between modules
917
+ * This pattern must be applied to all Stone libraries or third party libraries.
921
918
  */
922
- function defineAdapterErrorPage(module, options) {
923
- const error = options?.error ?? 'default';
924
- const adapterErrorPages = Object.fromEntries([error].flat().map((err) => [
925
- err,
926
- {
927
- ...options,
928
- module,
929
- error: err,
930
- isFactory: options?.isClass !== true
931
- }
932
- ]));
933
- return {
934
- stone: {
935
- useReact: {
936
- adapterErrorPages
937
- }
938
- }
939
- };
940
- }
919
+ /**
920
+ * A unique symbol key to mark classes as React Page component.
921
+ */
922
+ const REACT_PAGE_KEY = Symbol.for('ReactPage');
923
+ /**
924
+ * A unique symbol key to mark classes as React Page layout component.
925
+ */
926
+ const REACT_PAGE_LAYOUT_KEY = Symbol.for('ReactPageLayout');
927
+ /**
928
+ * A unique symbol key to mark classes as React Error handler component.
929
+ */
930
+ const REACT_ERROR_PAGE_KEY = Symbol.for('ReactErrorPage');
931
+ /**
932
+ * A unique symbol key to mark classes as React Adapter Error handler component.
933
+ */
934
+ const REACT_ADAPTER_ERROR_PAGE_KEY = Symbol.for('ReactAdapterErrorPage');
935
+ /**
936
+ * A unique symbol key to mark classes as React Stone application entry point.
937
+ */
938
+ const STONE_REACT_APP_KEY = Symbol.for('StoneReactApp');
941
939
 
942
940
  /**
943
- * Default blueprint for a React-based Stone.js application.
941
+ * A class decorator for defining a class as a React Handler layout.
944
942
  *
945
- * - Defines middleware, lifecycle hooks, and the default HTML template path.
943
+ * @param options - Configuration options for the layout definition.
944
+ * @returns A method decorator to be applied to a class method.
945
+ *
946
+ * @example
947
+ * ```typescript
948
+ * import { AdapterErrorPage } from '@stone-js/use-react';
949
+ *
950
+ * @AdapterErrorPage({ error: 'UserNotFoundError' })
951
+ * class UserAdapterErrorPage {
952
+ * render({ error }) {
953
+ * return <h1>User name: {error.message}</h1>;
954
+ * }
955
+ * }
956
+ * ```
946
957
  */
947
- const internalUseReactBlueprint = {
948
- stone: {
949
- useReact: {},
950
- services: [MetaReactRuntime],
951
- providers: [MetaUseReactServiceProvider]
952
- }
958
+ const AdapterErrorPage = (options) => {
959
+ return classDecoratorLegacyWrapper((_target, context) => {
960
+ setMetadata(context, REACT_ADAPTER_ERROR_PAGE_KEY, { ...options, isClass: true });
961
+ });
953
962
  };
954
963
 
955
964
  /**
956
- * Defines a Stone React app using a factory-based or class-based main handler.
965
+ * A class decorator for defining a class as a React Handler layout.
957
966
  *
958
- * @param moduleOrOptions - A factory function or class constructor for the main page.
959
- * @param optionsOrBlueprints - Optional application-level configuration.
960
- * @param maybeBlueprints - Additional blueprints to merge.
961
- * @returns A fully merged Stone blueprint.
967
+ * @param options - Configuration options for the layout definition.
968
+ * @returns A method decorator to be applied to a class method.
969
+ *
970
+ * @example
971
+ * ```typescript
972
+ * import { ErrorPage } from '@stone-js/use-react';
973
+ *
974
+ * @ErrorPage({ error: 'UserNotFoundError' })
975
+ * class UserErrorPage {
976
+ * render({ error }) {
977
+ * return <h1>User name: {error.message}</h1>;
978
+ * }
979
+ * }
980
+ * ```
962
981
  */
963
- function defineStoneReactApp(moduleOrOptions = {}, optionsOrBlueprints, maybeBlueprints) {
964
- let module;
965
- let options = {};
966
- let blueprints = [];
967
- // Pattern: defineStoneReactApp(handler, options?, blueprints?)
968
- if (isFunctionModule(moduleOrOptions)) {
969
- module = moduleOrOptions;
970
- if (isObjectLikeModule(optionsOrBlueprints)) {
971
- options = optionsOrBlueprints;
972
- blueprints = Array.isArray(maybeBlueprints) ? maybeBlueprints : [];
973
- }
974
- }
975
- else if (isObjectLikeModule(moduleOrOptions)) { // Pattern: defineStoneReactApp(options, blueprints?)
976
- options = moduleOrOptions;
977
- blueprints = Array.isArray(optionsOrBlueprints) ? optionsOrBlueprints : [];
978
- }
979
- const stonePart = {
980
- ...options,
981
- useReact: {
982
- ...options.useReact
983
- }
984
- };
985
- if (isNotEmpty(module)) {
986
- stonePart.useReact.componentEventHandler = {
987
- module,
988
- isComponent: true,
989
- isClass: options.isClass,
990
- isFactory: options.isClass !== true
991
- };
992
- }
993
- return mergeBlueprints(stoneBlueprint, internalUseReactBlueprint, ...blueprints, { stone: stonePart });
994
- }
982
+ const ErrorPage = (options) => {
983
+ return classDecoratorLegacyWrapper((_target, context) => {
984
+ setMetadata(context, REACT_ERROR_PAGE_KEY, { ...options, isClass: true });
985
+ });
986
+ };
995
987
 
996
988
  /**
997
- * Utility function to define a page.
989
+ * Hook decorator to mark a method as a lifecycle hook
990
+ * And automatically add it to the global lifecycle hook registry.
998
991
  *
999
- * @param module - The EventHandler module.
1000
- * @param options - Page definition options.
1001
- * @returns The UseReactBlueprint.
1002
- */
1003
- function definePage(module, options) {
1004
- return {
1005
- stone: {
1006
- router: {
1007
- definitions: [
1008
- {
1009
- ...options,
1010
- method: GET,
1011
- methods: [],
1012
- children: undefined,
1013
- handler: {
1014
- module,
1015
- isComponent: true,
1016
- layout: options?.layout,
1017
- isClass: options?.isClass,
1018
- isFactory: options?.isClass !== true
1019
- }
1020
- }
1021
- ]
1022
- }
1023
- }
1024
- };
1025
- }
1026
- /**
1027
- * Utility function to define a page layout.
1028
- *
1029
- * @param module - The layout module.
1030
- * @param options - Optional page layout definition options.
1031
- * @returns The UseReactBlueprint.
1032
- */
1033
- function definePageLayout(module, options) {
1034
- const name = options?.name ?? 'default';
1035
- return {
1036
- stone: {
1037
- useReact: {
1038
- layout: {
1039
- [name]: {
1040
- module,
1041
- isClass: options?.isClass,
1042
- isFactory: options?.isClass !== true
1043
- }
1044
- }
1045
- }
1046
- }
1047
- };
1048
- }
1049
- /**
1050
- * Utility function to define an error page.
1051
- *
1052
- * @param module - The layout module.
1053
- * @param options - Optional page layout definition options.
1054
- * @returns The UseReactBlueprint.
1055
- */
1056
- function defineErrorPage(module, options) {
1057
- const error = options?.error ?? 'default';
1058
- const errorPages = Object.fromEntries([error].flat().map((err) => [
1059
- err,
1060
- {
1061
- ...options,
1062
- module,
1063
- error: err,
1064
- isFactory: options?.isClass !== true
1065
- }
1066
- ]));
1067
- return {
1068
- stone: {
1069
- useReact: {
1070
- errorPages
1071
- }
1072
- }
1073
- };
1074
- }
1075
-
1076
- /**
1077
- * Constants are defined here to prevent Circular dependency between modules
1078
- * This pattern must be applied to all Stone libraries or third party libraries.
1079
- */
1080
- /**
1081
- * A unique symbol key to mark classes as React Page component.
1082
- */
1083
- const REACT_PAGE_KEY = Symbol.for('ReactPage');
1084
- /**
1085
- * A unique symbol key to mark classes as React Page layout component.
1086
- */
1087
- const REACT_PAGE_LAYOUT_KEY = Symbol.for('ReactPageLayout');
1088
- /**
1089
- * A unique symbol key to mark classes as React Error handler component.
1090
- */
1091
- const REACT_ERROR_PAGE_KEY = Symbol.for('ReactErrorPage');
1092
- /**
1093
- * A unique symbol key to mark classes as React Adapter Error handler component.
1094
- */
1095
- const REACT_ADAPTER_ERROR_PAGE_KEY = Symbol.for('ReactAdapterErrorPage');
1096
- /**
1097
- * A unique symbol key to mark classes as React Stone application entry point.
1098
- */
1099
- const STONE_REACT_APP_KEY = Symbol.for('StoneReactApp');
1100
-
1101
- /**
1102
- * A class decorator for defining a class as a React Handler layout.
1103
- *
1104
- * @param options - Configuration options for the layout definition.
1105
- * @returns A method decorator to be applied to a class method.
1106
- *
1107
- * @example
1108
- * ```typescript
1109
- * import { ErrorPage } from '@stone-js/use-react';
1110
- *
1111
- * @ErrorPage({ error: 'UserNotFoundError' })
1112
- * class UserErrorPage {
1113
- * render({ error }) {
1114
- * return <h1>User name: {error.message}</h1>;
1115
- * }
1116
- * }
1117
- * ```
1118
- */
1119
- const ErrorPage = (options) => {
1120
- return classDecoratorLegacyWrapper((_target, context) => {
1121
- setMetadata(context, REACT_ERROR_PAGE_KEY, { ...options, isClass: true });
1122
- });
1123
- };
1124
-
1125
- /**
1126
- * Hook decorator to mark a method as a lifecycle hook
1127
- * And automatically add it to the global lifecycle hook registry.
1128
- *
1129
- * @example
1130
- * ```typescript
1131
- * class MyClass {
1132
- * // ...
1133
- * @Hook('onPreparingPage')
1134
- * onPreparingPage () {}
1135
- * }
1136
- * ```
1137
- *
1138
- * @param name - The name of the lifecycle hook.
1139
- * @returns A class decorator function that sets the metadata using the provided options.
992
+ * @example
993
+ * ```typescript
994
+ * class MyClass {
995
+ * // ...
996
+ * @Hook('onPreparingPage')
997
+ * onPreparingPage () {}
998
+ * }
999
+ * ```
1000
+ *
1001
+ * @param name - The name of the lifecycle hook.
1002
+ * @returns A class decorator function that sets the metadata using the provided options.
1140
1003
  */
1141
1004
  const Hook = (name) => {
1142
1005
  return methodDecoratorLegacyWrapper((_target, context) => {
@@ -1169,26 +1032,31 @@ const PageLayout = (options) => {
1169
1032
  };
1170
1033
 
1171
1034
  /**
1172
- * A class decorator for defining a class as a React Handler layout.
1035
+ * Decorator to create a snapshot of the current data.
1173
1036
  *
1174
- * @param options - Configuration options for the layout definition.
1175
- * @returns A method decorator to be applied to a class method.
1037
+ * @param name - The name of the snapshot.
1038
+ * @returns A method decorator.
1176
1039
  *
1177
1040
  * @example
1178
1041
  * ```typescript
1179
- * import { AdapterErrorPage } from '@stone-js/use-react';
1042
+ * import { Service } from '@stone-js/core';
1043
+ * import { Snapshot } from '@stone-js/use-react';
1180
1044
  *
1181
- * @AdapterErrorPage({ error: 'UserNotFoundError' })
1182
- * class UserAdapterErrorPage {
1183
- * render({ error }) {
1184
- * return <h1>User name: {error.message}</h1>;
1045
+ * @Service({ alias: 'userService' })
1046
+ * class UserService {
1047
+ * @Snapshot()
1048
+ * showProfile() {
1049
+ * return { name: 'John Doe' };
1185
1050
  * }
1186
1051
  * }
1187
1052
  * ```
1188
1053
  */
1189
- const AdapterErrorPage = (options) => {
1190
- return classDecoratorLegacyWrapper((_target, context) => {
1191
- setMetadata(context, REACT_ADAPTER_ERROR_PAGE_KEY, { ...options, isClass: true });
1054
+ const Snapshot = (name) => {
1055
+ return methodDecoratorLegacyWrapper((target, context) => {
1056
+ return async function (...args) {
1057
+ name = name ?? `${String(Object.getPrototypeOf(this).constructor.name)}.${String(context.name)}`;
1058
+ return await ReactRuntime.instance?.snapshot(name, () => target.apply(this, args));
1059
+ };
1192
1060
  });
1193
1061
  };
1194
1062
 
@@ -1256,35 +1124,6 @@ const Page = (path, options = {}) => {
1256
1124
  });
1257
1125
  };
1258
1126
 
1259
- /**
1260
- * Decorator to create a snapshot of the current data.
1261
- *
1262
- * @param name - The name of the snapshot.
1263
- * @returns A method decorator.
1264
- *
1265
- * @example
1266
- * ```typescript
1267
- * import { Service } from '@stone-js/core';
1268
- * import { Snapshot } from '@stone-js/use-react';
1269
- *
1270
- * @Service({ alias: 'userService' })
1271
- * class UserService {
1272
- * @Snapshot()
1273
- * showProfile() {
1274
- * return { name: 'John Doe' };
1275
- * }
1276
- * }
1277
- * ```
1278
- */
1279
- const Snapshot = (name) => {
1280
- return methodDecoratorLegacyWrapper((target, context) => {
1281
- return async function (...args) {
1282
- name = name ?? `${String(Object.getPrototypeOf(this).constructor.name)}.${String(context.name)}`;
1283
- return await ReactRuntime.instance?.snapshot(name, () => target.apply(this, args));
1284
- };
1285
- });
1286
- };
1287
-
1288
1127
  /**
1289
1128
  * Sets the error handler for the React adapter and registers error pages.
1290
1129
  *
@@ -1320,76 +1159,6 @@ function setUseReactAdapterErrorHandler(errorHandler, context) {
1320
1159
  return context;
1321
1160
  }
1322
1161
 
1323
- /**
1324
- * Create an UseReact response.
1325
- *
1326
- * @param options - The options for creating the response.
1327
- * @returns The React response.
1328
- */
1329
- const reactResponse = (options) => {
1330
- if (isNotEmpty(options) &&
1331
- (isNotEmpty(options.url) ||
1332
- (isNotEmpty(options.content) && isNotEmpty(options.content.redirect)))) {
1333
- return reactRedirectResponse(options);
1334
- }
1335
- return OutgoingHttpResponse.create(options);
1336
- };
1337
- /**
1338
- * Create an UseReact redirect response.
1339
- *
1340
- * @param options - The options for creating the response.
1341
- * @returns The React redirect response.
1342
- */
1343
- const reactRedirectResponse = (options) => {
1344
- return RedirectResponse.create({ statusCode: 302, ...options });
1345
- };
1346
-
1347
- /**
1348
- * Class representing an UseReactServerErrorHandler.
1349
- *
1350
- * Adapter level error handler for React applications.
1351
- */
1352
- class UseReactServerErrorHandler {
1353
- logger;
1354
- blueprint;
1355
- /**
1356
- * Create an UseReactServerErrorHandler.
1357
- *
1358
- * @param options - UseReactServerErrorHandler options.
1359
- */
1360
- constructor({ blueprint }) {
1361
- this.blueprint = blueprint;
1362
- this.logger = Logger.getInstance();
1363
- }
1364
- /**
1365
- * Handle an error.
1366
- *
1367
- * @param error - The error to handle.
1368
- * @param context - The context of the adapter.
1369
- * @returns The raw response.
1370
- */
1371
- async handle(error, context) {
1372
- this.logger.error(error.message, { error });
1373
- return context
1374
- .rawResponseBuilder
1375
- .add('statusCode', error.statusCode ?? 500)
1376
- .add('headers', new Headers({ 'Content-Type': 'text/html' }))
1377
- .add('body', await this.getErrorBody(error, context));
1378
- }
1379
- /**
1380
- * Get the error body.
1381
- *
1382
- * @param error - The error to handle.
1383
- * @returns The error body.
1384
- */
1385
- async getErrorBody(error, context) {
1386
- const statusCode = error.statusCode ?? 500;
1387
- const template = htmlTemplate(this.blueprint);
1388
- const ClientApp = await buildAdapterErrorComponent(this.blueprint, context, statusCode, error);
1389
- return template.replace('<!--app-html-->', renderToString(ClientApp));
1390
- }
1391
- }
1392
-
1393
1162
  /**
1394
1163
  * Blueprint middleware to dynamically set lifecycle hooks for react.
1395
1164
  *
@@ -1521,6 +1290,237 @@ async function SetUseReactEventHandlerMiddleware(context, next) {
1521
1290
  return blueprint;
1522
1291
  }
1523
1292
 
1293
+ /**
1294
+ * Default blueprint for a React-based Stone.js application.
1295
+ *
1296
+ * - Defines middleware, lifecycle hooks, and the default HTML template path.
1297
+ */
1298
+ const internalUseReactBlueprint = {
1299
+ stone: {
1300
+ useReact: {},
1301
+ services: [MetaReactRuntime],
1302
+ providers: [MetaUseReactServiceProvider]
1303
+ }
1304
+ };
1305
+
1306
+ /**
1307
+ * Utility function to define an adapter error page.
1308
+ *
1309
+ * @param module - The adapter error page module.
1310
+ * @param options - Optional adapter error page options.
1311
+ * @returns The UseReactBlueprint.
1312
+ */
1313
+ function defineAdapterErrorPage(module, options) {
1314
+ const error = options?.error ?? 'default';
1315
+ const adapterErrorPages = Object.fromEntries([error].flat().map((err) => [
1316
+ err,
1317
+ {
1318
+ ...options,
1319
+ module,
1320
+ error: err,
1321
+ isFactory: options?.isClass !== true
1322
+ }
1323
+ ]));
1324
+ return {
1325
+ stone: {
1326
+ useReact: {
1327
+ adapterErrorPages
1328
+ }
1329
+ }
1330
+ };
1331
+ }
1332
+
1333
+ /**
1334
+ * Defines a Stone React app using a factory-based or class-based main handler.
1335
+ *
1336
+ * @param moduleOrOptions - A factory function or class constructor for the main page.
1337
+ * @param optionsOrBlueprints - Optional application-level configuration.
1338
+ * @param maybeBlueprints - Additional blueprints to merge.
1339
+ * @returns A fully merged Stone blueprint.
1340
+ */
1341
+ function defineStoneReactApp(moduleOrOptions = {}, optionsOrBlueprints, maybeBlueprints) {
1342
+ let module;
1343
+ let options = {};
1344
+ let blueprints = [];
1345
+ // Pattern: defineStoneReactApp(handler, options?, blueprints?)
1346
+ if (isFunctionModule(moduleOrOptions)) {
1347
+ module = moduleOrOptions;
1348
+ if (isObjectLikeModule(optionsOrBlueprints)) {
1349
+ options = optionsOrBlueprints;
1350
+ blueprints = Array.isArray(maybeBlueprints) ? maybeBlueprints : [];
1351
+ }
1352
+ }
1353
+ else if (isObjectLikeModule(moduleOrOptions)) { // Pattern: defineStoneReactApp(options, blueprints?)
1354
+ options = moduleOrOptions;
1355
+ blueprints = Array.isArray(optionsOrBlueprints) ? optionsOrBlueprints : [];
1356
+ }
1357
+ const stonePart = {
1358
+ ...options,
1359
+ useReact: {
1360
+ ...options.useReact
1361
+ }
1362
+ };
1363
+ if (isNotEmpty(module)) {
1364
+ stonePart.useReact.componentEventHandler = {
1365
+ module,
1366
+ isComponent: true,
1367
+ isClass: options.isClass,
1368
+ isFactory: options.isClass !== true
1369
+ };
1370
+ }
1371
+ return mergeBlueprints(stoneBlueprint, internalUseReactBlueprint, ...blueprints, { stone: stonePart });
1372
+ }
1373
+
1374
+ /**
1375
+ * Utility function to define a page.
1376
+ *
1377
+ * @param module - The EventHandler module.
1378
+ * @param options - Page definition options.
1379
+ * @returns The UseReactBlueprint.
1380
+ */
1381
+ function definePage(module, options) {
1382
+ return {
1383
+ stone: {
1384
+ router: {
1385
+ definitions: [
1386
+ {
1387
+ ...options,
1388
+ method: GET,
1389
+ methods: [],
1390
+ children: undefined,
1391
+ handler: {
1392
+ module,
1393
+ isComponent: true,
1394
+ layout: options?.layout,
1395
+ isClass: options?.isClass,
1396
+ isFactory: options?.isClass !== true
1397
+ }
1398
+ }
1399
+ ]
1400
+ }
1401
+ }
1402
+ };
1403
+ }
1404
+ /**
1405
+ * Utility function to define a page layout.
1406
+ *
1407
+ * @param module - The layout module.
1408
+ * @param options - Optional page layout definition options.
1409
+ * @returns The UseReactBlueprint.
1410
+ */
1411
+ function definePageLayout(module, options) {
1412
+ const name = options?.name ?? 'default';
1413
+ return {
1414
+ stone: {
1415
+ useReact: {
1416
+ layout: {
1417
+ [name]: {
1418
+ module,
1419
+ isClass: options?.isClass,
1420
+ isFactory: options?.isClass !== true
1421
+ }
1422
+ }
1423
+ }
1424
+ }
1425
+ };
1426
+ }
1427
+ /**
1428
+ * Utility function to define an error page.
1429
+ *
1430
+ * @param module - The layout module.
1431
+ * @param options - Optional page layout definition options.
1432
+ * @returns The UseReactBlueprint.
1433
+ */
1434
+ function defineErrorPage(module, options) {
1435
+ const error = options?.error ?? 'default';
1436
+ const errorPages = Object.fromEntries([error].flat().map((err) => [
1437
+ err,
1438
+ {
1439
+ ...options,
1440
+ module,
1441
+ error: err,
1442
+ isFactory: options?.isClass !== true
1443
+ }
1444
+ ]));
1445
+ return {
1446
+ stone: {
1447
+ useReact: {
1448
+ errorPages
1449
+ }
1450
+ }
1451
+ };
1452
+ }
1453
+
1454
+ /**
1455
+ * Create an UseReact response.
1456
+ *
1457
+ * @param options - The options for creating the response.
1458
+ * @returns The React response.
1459
+ */
1460
+ const reactResponse = (options) => {
1461
+ if (isNotEmpty(options) &&
1462
+ (isNotEmpty(options.url) ||
1463
+ (isNotEmpty(options.content) && isNotEmpty(options.content.redirect)))) {
1464
+ return reactRedirectResponse(options);
1465
+ }
1466
+ return OutgoingHttpResponse.create(options);
1467
+ };
1468
+ /**
1469
+ * Create an UseReact redirect response.
1470
+ *
1471
+ * @param options - The options for creating the response.
1472
+ * @returns The React redirect response.
1473
+ */
1474
+ const reactRedirectResponse = (options) => {
1475
+ return RedirectResponse.create({ statusCode: 302, ...options });
1476
+ };
1477
+
1478
+ /**
1479
+ * Class representing an UseReactServerErrorHandler.
1480
+ *
1481
+ * Adapter level error handler for React applications.
1482
+ */
1483
+ class UseReactServerErrorHandler {
1484
+ logger;
1485
+ blueprint;
1486
+ /**
1487
+ * Create an UseReactServerErrorHandler.
1488
+ *
1489
+ * @param options - UseReactServerErrorHandler options.
1490
+ */
1491
+ constructor({ blueprint }) {
1492
+ this.blueprint = blueprint;
1493
+ this.logger = Logger.getInstance();
1494
+ }
1495
+ /**
1496
+ * Handle an error.
1497
+ *
1498
+ * @param error - The error to handle.
1499
+ * @param context - The context of the adapter.
1500
+ * @returns The raw response.
1501
+ */
1502
+ async handle(error, context) {
1503
+ this.logger.error(error.message, { error });
1504
+ return context
1505
+ .rawResponseBuilder
1506
+ .add('statusCode', error.statusCode ?? 500)
1507
+ .add('headers', new Headers({ 'Content-Type': 'text/html' }))
1508
+ .add('body', await this.getErrorBody(error, context));
1509
+ }
1510
+ /**
1511
+ * Get the error body.
1512
+ *
1513
+ * @param error - The error to handle.
1514
+ * @returns The error body.
1515
+ */
1516
+ async getErrorBody(error, context) {
1517
+ const statusCode = error.statusCode ?? 500;
1518
+ const template = htmlTemplate(this.blueprint);
1519
+ const ClientApp = await buildAdapterErrorComponent(this.blueprint, context, statusCode, error);
1520
+ return template.replace('<!--app-html-->', renderToString(ClientApp));
1521
+ }
1522
+ }
1523
+
1524
1524
  /**
1525
1525
  * Blueprint middleware to process and register adapter error page definitions from modules.
1526
1526
  *