@goliapkg/sentori-next 0.1.0 → 0.2.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/README.md CHANGED
@@ -64,11 +64,16 @@ export default function RootLayout({ children }: { children: React.ReactNode })
64
64
  ```tsx
65
65
  // app/error.tsx
66
66
  'use client'
67
- import { SentoriErrorBoundary } from '@goliapkg/sentori-next/client'
68
-
69
- export default function Error({ error, reset }: { error: Error; reset: () => void }) {
70
- // Next already calls our boundary; here we render the fallback UI.
71
- // The capture happened upstream via SentoriProvider's hook.
67
+ import { useReportNextError } from '@goliapkg/sentori-next/app-router'
68
+
69
+ export default function Error({
70
+ error,
71
+ reset,
72
+ }: {
73
+ error: Error & { digest?: string }
74
+ reset: () => void
75
+ }) {
76
+ useReportNextError(error) // captureError once per error instance
72
77
  return (
73
78
  <div>
74
79
  <h2>Something went wrong</h2>
@@ -78,7 +83,28 @@ export default function Error({ error, reset }: { error: Error; reset: () => voi
78
83
  }
79
84
  ```
80
85
 
81
- For a global catch-all, do the same in `app/global-error.tsx`.
86
+ `useReportNextError` calls `captureError` once per error instance
87
+ and picks up Next's `error.digest` as a tag so the dashboard can
88
+ correlate the client report with the server error.
89
+
90
+ For a global catch-all, drop the same component into
91
+ `app/global-error.tsx`.
92
+
93
+ ### Navigation breadcrumbs (any layout)
94
+
95
+ ```tsx
96
+ // app/Shell.tsx — client wrapper mounted from app/layout.tsx
97
+ 'use client'
98
+ import { useNextRouter } from '@goliapkg/sentori-next/app-router'
99
+
100
+ export function Shell({ children }: { children: React.ReactNode }) {
101
+ useNextRouter() // nav breadcrumb on every pathname change
102
+ return <>{children}</>
103
+ }
104
+ ```
105
+
106
+ First mount does not emit a breadcrumb; only real pathname
107
+ transitions are recorded.
82
108
 
83
109
  ## What gets captured
84
110
 
@@ -102,6 +128,7 @@ the same signature. `serverInit()` is Node-only because Edge lacks
102
128
  | `@goliapkg/sentori-next/client` | App Router client components, `clientInit`, `SentoriProvider`, `<SentoriErrorBoundary>`, hooks |
103
129
  | `@goliapkg/sentori-next/server` | `instrumentation.ts`, `serverInit`, `onRequestError` |
104
130
  | `@goliapkg/sentori-next/instrumentation` | one-line `instrumentation.ts` re-export |
131
+ | `@goliapkg/sentori-next/app-router` | `useNextRouter`, `useReportNextError` — client-only App Router hooks |
105
132
 
106
133
  ## Versioning
107
134
 
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Subscribe to App Router pathname transitions and push a `nav`
3
+ * breadcrumb on every change. Mount once in a layout's client
4
+ * component (e.g. a `<Shell>` in `app/layout.tsx`):
5
+ *
6
+ * 'use client'
7
+ * import { useNextRouter } from '@goliapkg/sentori-next/app-router'
8
+ * export function Shell({ children }: { children: React.ReactNode }) {
9
+ * useNextRouter()
10
+ * return children
11
+ * }
12
+ *
13
+ * First mount does NOT emit a breadcrumb — only real transitions.
14
+ *
15
+ * Intentionally does not read `useSearchParams()` — that hook
16
+ * requires a Suspense boundary in Next.js 14+ which complicates
17
+ * adoption. Pathname alone covers the breadcrumb story.
18
+ */
19
+ export declare function useNextRouter(): void;
20
+ /**
21
+ * Capture an error from an App Router `error.tsx` file. Idiomatic
22
+ * usage:
23
+ *
24
+ * // app/error.tsx
25
+ * 'use client'
26
+ * import { useReportNextError } from '@goliapkg/sentori-next/app-router'
27
+ * export default function ErrorPage({ error, reset }: {
28
+ * error: Error & { digest?: string }
29
+ * reset: () => void
30
+ * }) {
31
+ * useReportNextError(error)
32
+ * return (
33
+ * <div>
34
+ * <h2>Something went wrong.</h2>
35
+ * <button onClick={reset}>Try again</button>
36
+ * </div>
37
+ * )
38
+ * }
39
+ *
40
+ * The hook calls captureError exactly once per error instance
41
+ * (subsequent renders with the same error are no-ops). Picks up
42
+ * Next's `digest` field as a tag so the dashboard can correlate
43
+ * the client report with the server error.
44
+ */
45
+ export declare function useReportNextError(error: Error & {
46
+ digest?: string;
47
+ }): void;
48
+ //# sourceMappingURL=app-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-router.d.ts","sourceRoot":"","sources":["../src/app-router.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAWpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAY3E"}
@@ -0,0 +1,77 @@
1
+ // App Router helpers for Next.js 14+. Imports from `next/navigation`
2
+ // (Next-side), so this module is client-only and is published under
3
+ // the `/app-router` subpath so the server bundle never tries to load
4
+ // `next/navigation` during instrumentation.
5
+ 'use client';
6
+ import { addBreadcrumb, captureError } from '@goliapkg/sentori-javascript';
7
+ import { usePathname } from 'next/navigation';
8
+ import { useEffect, useRef } from 'react';
9
+ /**
10
+ * Subscribe to App Router pathname transitions and push a `nav`
11
+ * breadcrumb on every change. Mount once in a layout's client
12
+ * component (e.g. a `<Shell>` in `app/layout.tsx`):
13
+ *
14
+ * 'use client'
15
+ * import { useNextRouter } from '@goliapkg/sentori-next/app-router'
16
+ * export function Shell({ children }: { children: React.ReactNode }) {
17
+ * useNextRouter()
18
+ * return children
19
+ * }
20
+ *
21
+ * First mount does NOT emit a breadcrumb — only real transitions.
22
+ *
23
+ * Intentionally does not read `useSearchParams()` — that hook
24
+ * requires a Suspense boundary in Next.js 14+ which complicates
25
+ * adoption. Pathname alone covers the breadcrumb story.
26
+ */
27
+ export function useNextRouter() {
28
+ const pathname = usePathname();
29
+ const prevRef = useRef(null);
30
+ useEffect(() => {
31
+ const prev = prevRef.current;
32
+ if (prev !== null && prev !== pathname) {
33
+ addBreadcrumb({ data: { from: prev, to: pathname }, type: 'nav' });
34
+ }
35
+ prevRef.current = pathname;
36
+ }, [pathname]);
37
+ }
38
+ /**
39
+ * Capture an error from an App Router `error.tsx` file. Idiomatic
40
+ * usage:
41
+ *
42
+ * // app/error.tsx
43
+ * 'use client'
44
+ * import { useReportNextError } from '@goliapkg/sentori-next/app-router'
45
+ * export default function ErrorPage({ error, reset }: {
46
+ * error: Error & { digest?: string }
47
+ * reset: () => void
48
+ * }) {
49
+ * useReportNextError(error)
50
+ * return (
51
+ * <div>
52
+ * <h2>Something went wrong.</h2>
53
+ * <button onClick={reset}>Try again</button>
54
+ * </div>
55
+ * )
56
+ * }
57
+ *
58
+ * The hook calls captureError exactly once per error instance
59
+ * (subsequent renders with the same error are no-ops). Picks up
60
+ * Next's `digest` field as a tag so the dashboard can correlate
61
+ * the client report with the server error.
62
+ */
63
+ export function useReportNextError(error) {
64
+ const seenRef = useRef(null);
65
+ useEffect(() => {
66
+ if (seenRef.current === error)
67
+ return;
68
+ seenRef.current = error;
69
+ captureError(error, {
70
+ tags: {
71
+ 'next.digest': error.digest ?? '',
72
+ source: 'next.error.tsx',
73
+ },
74
+ });
75
+ }, [error]);
76
+ }
77
+ //# sourceMappingURL=app-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-router.js","sourceRoot":"","sources":["../src/app-router.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,oEAAoE;AACpE,qEAAqE;AACrE,4CAA4C;AAE5C,YAAY,CAAA;AAEZ,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,MAAM,OAAO,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAA;IAE3C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAA;QAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAA;IAC5B,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkC;IACnE,MAAM,OAAO,GAAG,MAAM,CAAe,IAAI,CAAC,CAAA;IAC1C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK;YAAE,OAAM;QACrC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAA;QACvB,YAAY,CAAC,KAAK,EAAE;YAClB,IAAI,EAAE;gBACJ,aAAa,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBACjC,MAAM,EAAE,gBAAgB;aACzB;SACF,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;AACb,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goliapkg/sentori-next",
3
- "version": "0.1.0",
4
- "description": "Next.js adapter for Sentori — instrumentation.ts hooks, App Router error boundary, env-driven provider built on @goliapkg/sentori-react.",
3
+ "version": "0.2.1",
4
+ "description": "Next.js adapter for Sentori — instrumentation.ts hooks, App Router error boundary, navigation tracing, env-driven provider built on @goliapkg/sentori-react.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sentori.golia.jp",
7
7
  "repository": {
@@ -38,6 +38,10 @@
38
38
  "./instrumentation": {
39
39
  "types": "./lib/instrumentation.d.ts",
40
40
  "default": "./lib/instrumentation.js"
41
+ },
42
+ "./app-router": {
43
+ "types": "./lib/app-router.d.ts",
44
+ "default": "./lib/app-router.js"
41
45
  }
42
46
  },
43
47
  "files": [
@@ -56,9 +60,9 @@
56
60
  "react": ">=18"
57
61
  },
58
62
  "dependencies": {
59
- "@goliapkg/sentori-core": "0.1.0",
60
- "@goliapkg/sentori-javascript": "0.2.0",
61
- "@goliapkg/sentori-react": "0.1.0"
63
+ "@goliapkg/sentori-core": "0.3.0",
64
+ "@goliapkg/sentori-javascript": "0.3.1",
65
+ "@goliapkg/sentori-react": "0.4.1"
62
66
  },
63
67
  "devDependencies": {
64
68
  "@types/bun": "latest",
@@ -0,0 +1,80 @@
1
+ // App Router helpers for Next.js 14+. Imports from `next/navigation`
2
+ // (Next-side), so this module is client-only and is published under
3
+ // the `/app-router` subpath so the server bundle never tries to load
4
+ // `next/navigation` during instrumentation.
5
+
6
+ 'use client'
7
+
8
+ import { addBreadcrumb, captureError } from '@goliapkg/sentori-javascript'
9
+ import { usePathname } from 'next/navigation'
10
+ import { useEffect, useRef } from 'react'
11
+
12
+ /**
13
+ * Subscribe to App Router pathname transitions and push a `nav`
14
+ * breadcrumb on every change. Mount once in a layout's client
15
+ * component (e.g. a `<Shell>` in `app/layout.tsx`):
16
+ *
17
+ * 'use client'
18
+ * import { useNextRouter } from '@goliapkg/sentori-next/app-router'
19
+ * export function Shell({ children }: { children: React.ReactNode }) {
20
+ * useNextRouter()
21
+ * return children
22
+ * }
23
+ *
24
+ * First mount does NOT emit a breadcrumb — only real transitions.
25
+ *
26
+ * Intentionally does not read `useSearchParams()` — that hook
27
+ * requires a Suspense boundary in Next.js 14+ which complicates
28
+ * adoption. Pathname alone covers the breadcrumb story.
29
+ */
30
+ export function useNextRouter(): void {
31
+ const pathname = usePathname()
32
+ const prevRef = useRef<null | string>(null)
33
+
34
+ useEffect(() => {
35
+ const prev = prevRef.current
36
+ if (prev !== null && prev !== pathname) {
37
+ addBreadcrumb({ data: { from: prev, to: pathname }, type: 'nav' })
38
+ }
39
+ prevRef.current = pathname
40
+ }, [pathname])
41
+ }
42
+
43
+ /**
44
+ * Capture an error from an App Router `error.tsx` file. Idiomatic
45
+ * usage:
46
+ *
47
+ * // app/error.tsx
48
+ * 'use client'
49
+ * import { useReportNextError } from '@goliapkg/sentori-next/app-router'
50
+ * export default function ErrorPage({ error, reset }: {
51
+ * error: Error & { digest?: string }
52
+ * reset: () => void
53
+ * }) {
54
+ * useReportNextError(error)
55
+ * return (
56
+ * <div>
57
+ * <h2>Something went wrong.</h2>
58
+ * <button onClick={reset}>Try again</button>
59
+ * </div>
60
+ * )
61
+ * }
62
+ *
63
+ * The hook calls captureError exactly once per error instance
64
+ * (subsequent renders with the same error are no-ops). Picks up
65
+ * Next's `digest` field as a tag so the dashboard can correlate
66
+ * the client report with the server error.
67
+ */
68
+ export function useReportNextError(error: Error & { digest?: string }): void {
69
+ const seenRef = useRef<Error | null>(null)
70
+ useEffect(() => {
71
+ if (seenRef.current === error) return
72
+ seenRef.current = error
73
+ captureError(error, {
74
+ tags: {
75
+ 'next.digest': error.digest ?? '',
76
+ source: 'next.error.tsx',
77
+ },
78
+ })
79
+ }, [error])
80
+ }