@alepha/react 0.13.6 → 0.13.7

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.
@@ -200,150 +200,388 @@ var ClientOnly_default = ClientOnly;
200
200
 
201
201
  //#endregion
202
202
  //#region ../../src/core/components/ErrorViewer.tsx
203
+ /**
204
+ * Error viewer component that displays error details in development mode
205
+ */
203
206
  const ErrorViewer = ({ error, alepha }) => {
204
207
  const [expanded, setExpanded] = useState(false);
205
208
  if (alepha.isProduction()) return /* @__PURE__ */ jsx(ErrorViewerProduction, {});
206
- const stackLines = error.stack?.split("\n") ?? [];
207
- const previewLines = stackLines.slice(0, 5);
208
- const hiddenLineCount = stackLines.length - previewLines.length;
209
- const copyToClipboard = (text) => {
210
- navigator.clipboard.writeText(text).catch((err) => {
211
- console.error("Clipboard error:", err);
212
- });
209
+ const frames = parseStackTrace(error.stack);
210
+ const visibleFrames = expanded ? frames : frames.slice(0, 6);
211
+ const hiddenCount = frames.length - 6;
212
+ return /* @__PURE__ */ jsx("div", {
213
+ style: styles.overlay,
214
+ children: /* @__PURE__ */ jsxs("div", {
215
+ style: styles.container,
216
+ children: [/* @__PURE__ */ jsx(Header, { error }), /* @__PURE__ */ jsx(StackTraceSection, {
217
+ frames,
218
+ visibleFrames,
219
+ expanded,
220
+ hiddenCount,
221
+ onToggle: () => setExpanded(!expanded)
222
+ })]
223
+ })
224
+ });
225
+ };
226
+ var ErrorViewer_default = ErrorViewer;
227
+ /**
228
+ * Parse stack trace string into structured frames
229
+ */
230
+ function parseStackTrace(stack) {
231
+ if (!stack) return [];
232
+ const lines = stack.split("\n").slice(1);
233
+ const frames = [];
234
+ for (const line of lines) {
235
+ const trimmed = line.trim();
236
+ if (!trimmed.startsWith("at ")) continue;
237
+ const frame = parseStackLine(trimmed);
238
+ if (frame) frames.push(frame);
239
+ }
240
+ return frames;
241
+ }
242
+ /**
243
+ * Parse a single stack trace line into a structured frame
244
+ */
245
+ function parseStackLine(line) {
246
+ const withFn = line.match(/^at\s+(.+?)\s+\((.+):(\d+):(\d+)\)$/);
247
+ if (withFn) return {
248
+ fn: withFn[1],
249
+ file: withFn[2],
250
+ line: withFn[3],
251
+ col: withFn[4],
252
+ raw: line
213
253
  };
214
- const styles = {
215
- container: {
216
- padding: "24px",
217
- backgroundColor: "#FEF2F2",
218
- color: "#7F1D1D",
219
- border: "1px solid #FECACA",
220
- borderRadius: "16px",
221
- boxShadow: "0 8px 24px rgba(0,0,0,0.05)",
222
- fontFamily: "monospace",
223
- maxWidth: "768px",
224
- margin: "40px auto"
225
- },
226
- heading: {
227
- fontSize: "20px",
228
- fontWeight: "bold",
229
- marginBottom: "10px"
230
- },
231
- name: {
232
- fontSize: "16px",
233
- fontWeight: 600
234
- },
235
- message: {
236
- fontSize: "14px",
237
- marginBottom: "16px"
238
- },
239
- sectionHeader: {
240
- display: "flex",
241
- justifyContent: "space-between",
242
- alignItems: "center",
243
- fontSize: "12px",
244
- marginBottom: "4px",
245
- color: "#991B1B"
246
- },
247
- copyButton: {
248
- fontSize: "12px",
249
- color: "#DC2626",
250
- background: "none",
251
- border: "none",
252
- cursor: "pointer",
253
- textDecoration: "underline"
254
- },
255
- stackContainer: {
256
- backgroundColor: "#FEE2E2",
257
- padding: "12px",
258
- borderRadius: "8px",
259
- fontSize: "13px",
260
- lineHeight: "1.4",
261
- overflowX: "auto",
262
- whiteSpace: "pre-wrap"
263
- },
264
- expandLine: {
265
- color: "#F87171",
266
- cursor: "pointer",
267
- marginTop: "8px"
268
- }
254
+ const withoutFn = line.match(/^at\s+(.+):(\d+):(\d+)$/);
255
+ if (withoutFn) return {
256
+ fn: "<anonymous>",
257
+ file: withoutFn[1],
258
+ line: withoutFn[2],
259
+ col: withoutFn[3],
260
+ raw: line
261
+ };
262
+ return {
263
+ fn: "",
264
+ file: line.replace(/^at\s+/, ""),
265
+ line: "",
266
+ col: "",
267
+ raw: line
268
+ };
269
+ }
270
+ /**
271
+ * Copy text to clipboard
272
+ */
273
+ function copyToClipboard(text) {
274
+ navigator.clipboard.writeText(text).catch((err) => {
275
+ console.error("Clipboard error:", err);
276
+ });
277
+ }
278
+ /**
279
+ * Header section with error type and message
280
+ */
281
+ function Header({ error }) {
282
+ const [copied, setCopied] = useState(false);
283
+ const handleCopy = () => {
284
+ copyToClipboard(error.stack || error.message);
285
+ setCopied(true);
286
+ setTimeout(() => setCopied(false), 2e3);
269
287
  };
270
288
  return /* @__PURE__ */ jsxs("div", {
271
- style: styles.container,
272
- children: [/* @__PURE__ */ jsxs("div", { children: [
273
- /* @__PURE__ */ jsx("div", {
274
- style: styles.heading,
275
- children: "🔥 Error"
276
- }),
277
- /* @__PURE__ */ jsx("div", {
278
- style: styles.name,
289
+ style: styles.header,
290
+ children: [/* @__PURE__ */ jsxs("div", {
291
+ style: styles.headerTop,
292
+ children: [/* @__PURE__ */ jsx("div", {
293
+ style: styles.badge,
279
294
  children: error.name
280
- }),
281
- /* @__PURE__ */ jsx("div", {
282
- style: styles.message,
283
- children: error.message
284
- })
285
- ] }), stackLines.length > 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
286
- style: styles.sectionHeader,
287
- children: [/* @__PURE__ */ jsx("span", { children: "Stack trace" }), /* @__PURE__ */ jsx("button", {
295
+ }), /* @__PURE__ */ jsx("button", {
288
296
  type: "button",
289
- onClick: () => copyToClipboard(error.stack),
290
- style: styles.copyButton,
291
- children: "Copy all"
297
+ onClick: handleCopy,
298
+ style: styles.copyBtn,
299
+ children: copied ? "Copied" : "Copy Stack"
292
300
  })]
293
- }), /* @__PURE__ */ jsxs("pre", {
294
- style: styles.stackContainer,
295
- children: [(expanded ? stackLines : previewLines).map((line, i) => /* @__PURE__ */ jsx("div", { children: line }, i)), !expanded && hiddenLineCount > 0 && /* @__PURE__ */ jsxs("div", {
296
- style: styles.expandLine,
297
- onClick: () => setExpanded(true),
298
- children: [
299
- "+ ",
300
- hiddenLineCount,
301
- " more lines..."
302
- ]
303
- })]
304
- })] })]
301
+ }), /* @__PURE__ */ jsx("h1", {
302
+ style: styles.message,
303
+ children: error.message
304
+ })]
305
305
  });
306
- };
307
- var ErrorViewer_default = ErrorViewer;
308
- const ErrorViewerProduction = () => {
309
- const styles = {
310
- container: {
311
- padding: "24px",
312
- backgroundColor: "#FEF2F2",
313
- color: "#7F1D1D",
314
- border: "1px solid #FECACA",
315
- borderRadius: "16px",
316
- boxShadow: "0 8px 24px rgba(0,0,0,0.05)",
317
- fontFamily: "monospace",
318
- maxWidth: "768px",
319
- margin: "40px auto",
320
- textAlign: "center"
321
- },
322
- heading: {
323
- fontSize: "20px",
324
- fontWeight: "bold",
325
- marginBottom: "8px"
326
- },
327
- name: {
328
- fontSize: "16px",
329
- fontWeight: 600,
330
- marginBottom: "4px"
331
- },
332
- message: {
333
- fontSize: "14px",
334
- opacity: .85
335
- }
336
- };
306
+ }
307
+ /**
308
+ * Stack trace section with expandable frames
309
+ */
310
+ function StackTraceSection({ frames, visibleFrames, expanded, hiddenCount, onToggle }) {
311
+ if (frames.length === 0) return null;
337
312
  return /* @__PURE__ */ jsxs("div", {
338
- style: styles.container,
313
+ style: styles.stackSection,
339
314
  children: [/* @__PURE__ */ jsx("div", {
340
- style: styles.heading,
341
- children: "🚨 An error occurred"
342
- }), /* @__PURE__ */ jsx("div", {
343
- style: styles.message,
344
- children: "Something went wrong. Please try again later."
315
+ style: styles.stackHeader,
316
+ children: "Call Stack"
317
+ }), /* @__PURE__ */ jsxs("div", {
318
+ style: styles.frameList,
319
+ children: [
320
+ visibleFrames.map((frame, i) => /* @__PURE__ */ jsx(StackFrameRow, {
321
+ frame,
322
+ index: i
323
+ }, i)),
324
+ !expanded && hiddenCount > 0 && /* @__PURE__ */ jsxs("button", {
325
+ type: "button",
326
+ onClick: onToggle,
327
+ style: styles.expandBtn,
328
+ children: [
329
+ "Show ",
330
+ hiddenCount,
331
+ " more frames"
332
+ ]
333
+ }),
334
+ expanded && hiddenCount > 0 && /* @__PURE__ */ jsx("button", {
335
+ type: "button",
336
+ onClick: onToggle,
337
+ style: styles.expandBtn,
338
+ children: "Show less"
339
+ })
340
+ ]
341
+ })]
342
+ });
343
+ }
344
+ /**
345
+ * Single stack frame row
346
+ */
347
+ function StackFrameRow({ frame, index }) {
348
+ const isFirst = index === 0;
349
+ const fileName = frame.file.split("/").pop() || frame.file;
350
+ const dirPath = frame.file.substring(0, frame.file.length - fileName.length);
351
+ return /* @__PURE__ */ jsxs("div", {
352
+ style: {
353
+ ...styles.frame,
354
+ ...isFirst ? styles.frameFirst : {}
355
+ },
356
+ children: [/* @__PURE__ */ jsx("div", {
357
+ style: styles.frameIndex,
358
+ children: index + 1
359
+ }), /* @__PURE__ */ jsxs("div", {
360
+ style: styles.frameContent,
361
+ children: [frame.fn && /* @__PURE__ */ jsx("div", {
362
+ style: styles.fnName,
363
+ children: frame.fn
364
+ }), /* @__PURE__ */ jsxs("div", {
365
+ style: styles.filePath,
366
+ children: [
367
+ /* @__PURE__ */ jsx("span", {
368
+ style: styles.dirPath,
369
+ children: dirPath
370
+ }),
371
+ /* @__PURE__ */ jsx("span", {
372
+ style: styles.fileName,
373
+ children: fileName
374
+ }),
375
+ frame.line && /* @__PURE__ */ jsxs("span", {
376
+ style: styles.lineCol,
377
+ children: [
378
+ ":",
379
+ frame.line,
380
+ ":",
381
+ frame.col
382
+ ]
383
+ })
384
+ ]
385
+ })]
345
386
  })]
346
387
  });
388
+ }
389
+ /**
390
+ * Production error view - minimal information
391
+ */
392
+ function ErrorViewerProduction() {
393
+ return /* @__PURE__ */ jsx("div", {
394
+ style: styles.overlay,
395
+ children: /* @__PURE__ */ jsxs("div", {
396
+ style: styles.prodContainer,
397
+ children: [
398
+ /* @__PURE__ */ jsx("div", {
399
+ style: styles.prodIcon,
400
+ children: "!"
401
+ }),
402
+ /* @__PURE__ */ jsx("h1", {
403
+ style: styles.prodTitle,
404
+ children: "Application Error"
405
+ }),
406
+ /* @__PURE__ */ jsx("p", {
407
+ style: styles.prodMessage,
408
+ children: "An unexpected error occurred. Please try again later."
409
+ }),
410
+ /* @__PURE__ */ jsx("button", {
411
+ type: "button",
412
+ onClick: () => window.location.reload(),
413
+ style: styles.prodButton,
414
+ children: "Reload Page"
415
+ })
416
+ ]
417
+ })
418
+ });
419
+ }
420
+ const styles = {
421
+ overlay: {
422
+ position: "fixed",
423
+ inset: 0,
424
+ backgroundColor: "rgba(0, 0, 0, 0.8)",
425
+ display: "flex",
426
+ alignItems: "flex-start",
427
+ justifyContent: "center",
428
+ padding: "40px 20px",
429
+ overflow: "auto",
430
+ fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif",
431
+ zIndex: 99999
432
+ },
433
+ container: {
434
+ width: "100%",
435
+ maxWidth: "960px",
436
+ backgroundColor: "#1a1a1a",
437
+ borderRadius: "12px",
438
+ overflow: "hidden",
439
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)"
440
+ },
441
+ header: {
442
+ padding: "24px 28px",
443
+ borderBottom: "1px solid #333",
444
+ background: "linear-gradient(to bottom, #1f1f1f, #1a1a1a)"
445
+ },
446
+ headerTop: {
447
+ display: "flex",
448
+ alignItems: "center",
449
+ justifyContent: "space-between",
450
+ marginBottom: "16px"
451
+ },
452
+ badge: {
453
+ display: "inline-block",
454
+ padding: "6px 12px",
455
+ backgroundColor: "#dc2626",
456
+ color: "#fff",
457
+ fontSize: "12px",
458
+ fontWeight: 600,
459
+ borderRadius: "6px",
460
+ letterSpacing: "0.025em"
461
+ },
462
+ copyBtn: {
463
+ padding: "8px 16px",
464
+ backgroundColor: "transparent",
465
+ color: "#888",
466
+ fontSize: "13px",
467
+ fontWeight: 500,
468
+ border: "1px solid #444",
469
+ borderRadius: "6px",
470
+ cursor: "pointer",
471
+ transition: "all 0.15s"
472
+ },
473
+ message: {
474
+ margin: 0,
475
+ fontSize: "20px",
476
+ fontWeight: 500,
477
+ color: "#fff",
478
+ lineHeight: 1.5,
479
+ wordBreak: "break-word"
480
+ },
481
+ stackSection: { padding: "0" },
482
+ stackHeader: {
483
+ padding: "16px 28px",
484
+ fontSize: "11px",
485
+ fontWeight: 600,
486
+ color: "#666",
487
+ textTransform: "uppercase",
488
+ letterSpacing: "0.1em",
489
+ borderBottom: "1px solid #2a2a2a"
490
+ },
491
+ frameList: {
492
+ display: "flex",
493
+ flexDirection: "column"
494
+ },
495
+ frame: {
496
+ display: "flex",
497
+ alignItems: "flex-start",
498
+ padding: "14px 28px",
499
+ borderBottom: "1px solid #252525",
500
+ transition: "background-color 0.15s"
501
+ },
502
+ frameFirst: { backgroundColor: "rgba(220, 38, 38, 0.1)" },
503
+ frameIndex: {
504
+ width: "28px",
505
+ flexShrink: 0,
506
+ fontSize: "12px",
507
+ fontWeight: 500,
508
+ color: "#555",
509
+ fontFamily: "monospace"
510
+ },
511
+ frameContent: {
512
+ flex: 1,
513
+ minWidth: 0
514
+ },
515
+ fnName: {
516
+ fontSize: "14px",
517
+ fontWeight: 500,
518
+ color: "#e5e5e5",
519
+ marginBottom: "4px",
520
+ fontFamily: "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace"
521
+ },
522
+ filePath: {
523
+ fontSize: "13px",
524
+ color: "#888",
525
+ fontFamily: "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace",
526
+ wordBreak: "break-all"
527
+ },
528
+ dirPath: { color: "#555" },
529
+ fileName: { color: "#0ea5e9" },
530
+ lineCol: { color: "#eab308" },
531
+ expandBtn: {
532
+ padding: "16px 28px",
533
+ backgroundColor: "transparent",
534
+ color: "#666",
535
+ fontSize: "13px",
536
+ fontWeight: 500,
537
+ border: "none",
538
+ borderTop: "1px solid #252525",
539
+ cursor: "pointer",
540
+ textAlign: "left",
541
+ transition: "all 0.15s"
542
+ },
543
+ prodContainer: {
544
+ textAlign: "center",
545
+ padding: "60px 40px",
546
+ backgroundColor: "#1a1a1a",
547
+ borderRadius: "12px",
548
+ maxWidth: "400px"
549
+ },
550
+ prodIcon: {
551
+ width: "64px",
552
+ height: "64px",
553
+ margin: "0 auto 24px",
554
+ backgroundColor: "#dc2626",
555
+ borderRadius: "50%",
556
+ display: "flex",
557
+ alignItems: "center",
558
+ justifyContent: "center",
559
+ fontSize: "32px",
560
+ fontWeight: 700,
561
+ color: "#fff"
562
+ },
563
+ prodTitle: {
564
+ margin: "0 0 12px",
565
+ fontSize: "24px",
566
+ fontWeight: 600,
567
+ color: "#fff"
568
+ },
569
+ prodMessage: {
570
+ margin: "0 0 28px",
571
+ fontSize: "15px",
572
+ color: "#888",
573
+ lineHeight: 1.6
574
+ },
575
+ prodButton: {
576
+ padding: "12px 24px",
577
+ backgroundColor: "#fff",
578
+ color: "#000",
579
+ fontSize: "14px",
580
+ fontWeight: 600,
581
+ border: "none",
582
+ borderRadius: "8px",
583
+ cursor: "pointer"
584
+ }
347
585
  };
348
586
 
349
587
  //#endregion
@@ -509,8 +747,11 @@ var ErrorBoundary_default = ErrorBoundary;
509
747
  * ```
510
748
  */
511
749
  const NestedView = (props) => {
512
- const index = use(RouterLayerContext)?.index ?? 0;
750
+ const routerLayer = use(RouterLayerContext);
751
+ const index = routerLayer?.index ?? 0;
752
+ const onError = routerLayer?.onError;
513
753
  const state = useRouterState();
754
+ const alepha = useAlepha();
514
755
  const [view, setView] = useState(state.layers[index]?.element);
515
756
  const [animation, setAnimation] = useState("");
516
757
  const animationExitDuration = useRef(0);
@@ -572,12 +813,16 @@ const NestedView = (props) => {
572
813
  fallback: props.errorBoundary,
573
814
  children: element
574
815
  });
816
+ const fallback = (error) => {
817
+ const result = onError?.(error, state) ?? /* @__PURE__ */ jsx(ErrorViewer_default, {
818
+ error,
819
+ alepha
820
+ });
821
+ if (result instanceof Redirection) return "Redirection inside ErrorBoundary is not allowed.";
822
+ return result;
823
+ };
575
824
  return /* @__PURE__ */ jsx(ErrorBoundary_default, {
576
- fallback: (error) => {
577
- const result = state.onError(error, state);
578
- if (result instanceof Redirection) return "Redirection inside ErrorBoundary is not allowed.";
579
- return result;
580
- },
825
+ fallback,
581
826
  children: element
582
827
  });
583
828
  };
@@ -770,6 +1015,7 @@ var ReactPageProvider = class {
770
1015
  }
771
1016
  if (!it.error) try {
772
1017
  const element = await this.createElement(it.route, {
1018
+ ...it.route.props ? it.route.props() : {},
773
1019
  ...props,
774
1020
  ...context
775
1021
  });
@@ -811,9 +1057,6 @@ var ReactPageProvider = class {
811
1057
  }
812
1058
  return { state };
813
1059
  }
814
- createRedirectionLayer(redirect) {
815
- return { redirect };
816
- }
817
1060
  getErrorHandler(route) {
818
1061
  if (route.errorHandler) return route.errorHandler;
819
1062
  let parent = route.parent;
@@ -838,7 +1081,7 @@ var ReactPageProvider = class {
838
1081
  }
839
1082
  href(page, params = {}) {
840
1083
  const found = this.pages.find((it) => it.name === page.options.name);
841
- if (!found) throw new Error(`Page ${page.options.name} not found`);
1084
+ if (!found) throw new AlephaError(`Page ${page.options.name} not found`);
842
1085
  let url = found.path ?? "";
843
1086
  let parent = found.parent;
844
1087
  while (parent) {
@@ -857,7 +1100,8 @@ var ReactPageProvider = class {
857
1100
  const element = page.client ? createElement(ClientOnly_default, typeof page.client === "object" ? page.client : {}, view) : view;
858
1101
  return createElement(RouterLayerContext.Provider, { value: {
859
1102
  index,
860
- path
1103
+ path,
1104
+ onError: this.getErrorHandler(page) ?? ((error) => this.renderError(error))
861
1105
  } }, element);
862
1106
  }
863
1107
  configure = $hook({