sekisho 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,97 +10,50 @@
10
10
 
11
11
  See the [example-nextjs-app](../../packages/example-nextjs-app). This example is deployed online at [https://sekisho-demo.pages.dev](https://sekisho-demo.pages.dev).
12
12
 
13
- ### Simple setup
13
+ ### Login Redirects for Unauthenticated Users
14
14
 
15
- Wrap your app with `SekishoProvider` with options:
15
+ Use `NotAuthenticatedContainer` to handle login redirects in a way that works with your framework's routing constraints.
16
16
 
17
- ```tsx
18
- // app/providers.tsx
19
-
20
- // here we create a dedicated file, as we want to make this file a client component in Next.js App Router
21
- //
22
- // if you don't use the Next.js App Router, you can just wrap your app's entrypoint with `SekishoProvider`
23
- // directly without creating a separate file
24
-
25
- 'use client';
26
-
27
- import { SekishoProvider } from 'sekisho';
28
- import { useRouter } from 'next/navigation';
29
-
30
- export function Providers({ children }: React.PropsWithChildren) {
31
- const router = useRouter();
32
-
33
- return (
34
- <SekishoProvider
35
- onNeedLogin={() => {
36
- // Tell Sekisho what to do when the authentication is required.
37
- router.push('/login');
38
- // Sekisho can work with any router or navigation library.
39
- // You can also call `navigate` from React Router's `useNavigate` here:
40
- // navigate('/login');
41
- }}
42
- >
43
- {children}
44
- </SekishoProvider>
45
- );
46
- }
47
- ```
17
+ **Wrap your protected routes/content with `NotAuthenticatedContainer`**
48
18
 
49
19
  ```tsx
50
- // app/layout.tsx
51
- import { Providers } from './providers';
20
+ 'use client';
52
21
 
53
- export default function RootLayout({ children }: { children: React.ReactNode }) {
54
- return (
55
- <html lang="en">
56
- <body>
57
- <Providers>{children}</Providers>
58
- </body>
59
- </html>
60
- );
61
- }
62
- ```
22
+ import { NotAuthenticatedContainer } from 'sekisho';
63
23
 
64
- By default, `SekishoProvider` already includes `NotAuthenticatedBoundary` that will catch any `NotAuthenticatedError` thrown by `needLogin()` in the subtree. But if you have special error handling needs in certain parts of your app, you can also always import `NotAuthenticatedBoundary` directly to wrap those parts:
24
+ function LoginRedirect() {
25
+ // perform your redirect logic here, e.g. using your framework's router
26
+ // for example, in Next.js App Router:
27
+ return redirect('/login');
65
28
 
66
- ```tsx
67
- import { NotAuthenticatedBoundary } from 'sekisho';
29
+ // in React Router:
30
+ const navigate = useNavigate();
31
+ useEffect(() => { navigate('/login'); }, [navigate]);
32
+ return null;
33
+ // or
34
+ return <Navigate to="/login" />;
68
35
 
69
- function SomePartOfApp() {
70
- return (
71
- <NotAuthenticatedBoundary>
72
- {/* ... */}
73
- </NotAuthenticatedBoundary>
74
- );
36
+ // Wouter
37
+ return <Redirect to="/login" />;
75
38
  }
76
- ```
77
-
78
- And if you are using Next.js App Router and `error.tsx` file, due to Next.js layout, page, and error boundary heirarchy, you will also need to wrap the `error.tsx` with `NotAuthenticatedErrorWrapper`:
79
-
80
- ```tsx
81
- // app/error.tsx
82
- 'use client';
83
39
 
84
- import { NotAuthenticatedErrorWrapper } from 'sekisho';
85
-
86
- export default function ErrorPage({ error, reset }) {
40
+ export function Protected() {
87
41
  return (
88
- <NotAuthenticatedErrorWrapper error={error}>
89
- {/* Your existing error UI goes in here */}
90
- </NotAuthenticatedErrorWrapper>
42
+ <NotAuthenticatedContainer
43
+ // you can pass a React element directly as fallback
44
+ fallback={<LoginRedirect />}
45
+ // or you can pass a component that receives the error object as prop
46
+ fallbackComponent={LoginRedirect}
47
+ >
48
+ {/* Protected content goes here */}
49
+ </NotAuthenticatedContainer>
91
50
  );
92
51
  }
93
52
  ```
94
53
 
95
- > `NotAuthenticatedErrorWrapper` is actually used by `NotAuthenticatedBoundary` internally, containing all the core logic.
54
+ **Trigger a login redirect**
96
55
 
97
- ### Triggering a login redirect
98
-
99
- Call `needLogin()` anywhere during the React render phase.
100
-
101
- Right now, you can't call `needLogin()` within an event handler or `useEffect`, because it won't be caught by the React error boundary mechanism. We will be implementing this in a future version.
102
-
103
- **In a client component** (e.g. when session state is absent):
56
+ Call `needLogin()` anywhere during the React render phase. You can call `needLogin()` in a client component (e.g. when session state is absent):
104
57
 
105
58
  ```tsx
106
59
  import { needLogin } from 'sekisho';
@@ -116,7 +69,7 @@ function Dashboard() {
116
69
  }
117
70
  ```
118
71
 
119
- **In a [SWR](https://swr.vercel.app) middleware** (e.g. when an API response carries a known auth error):
72
+ ...or you can call `needLogin()` in a [SWR](https://swr.vercel.app) middleware (e.g. when an API response carries a known auth error):
120
73
 
121
74
  ```tsx
122
75
  import { needLogin } from 'sekisho';
@@ -131,7 +84,115 @@ export const requireAuthMiddleware: Middleware = (useSWRNext) => (key, fetcher,
131
84
  };
132
85
  ```
133
86
 
134
- ### Restricting access
87
+ > Right now, you can't call `needLogin()` within an event handler or `useEffect`, because it won't be caught by the React error boundary mechanism. We may be able to implement this in a future version.
88
+
89
+ This kinda like `<Suspense />` but for login redirects instead. And like `<Suspense />`, you can have multiple `NotAuthenticatedContainer`s nested independently — each one only catches the `needLogin()` calls within its own subtree.
90
+
91
+ **Set up with Next.js App Router**
92
+
93
+ You may use Next.js [Route Groups](https://nextjs.org/docs/app/api-reference/file-conventions/route-groups) to create protected and unprotected sections of your app:
94
+
95
+ ```
96
+ app/
97
+ ├── (protected)/ ← all protected routes goes under here
98
+ │ ├── layout.tsx ← wrap with <NotAuthenticatedContainer> here
99
+ │ ├── error.tsx ← wrap with <NotAuthenticatedErrorWrapper> if you are using error.tsx file convention
100
+ │ └── page.tsx ← homepage, where you call needLogin() when authentication is needed
101
+
102
+ ├── (unprotected)/ ← all unprotected routes goes under here
103
+ │ └── login/
104
+ │ └── page.tsx
105
+
106
+ └── layout.tsx ← your root layout with <html /> and <body />
107
+ ```
108
+
109
+ ```tsx
110
+ // app/(protected)/layout.tsx
111
+ import { NotAuthenticatedContainer } from 'sekisho';
112
+ import { redirect } from 'next/navigation';
113
+
114
+ function LoginRedirect(): never {
115
+ return redirect('/login');
116
+ }
117
+
118
+ export function ProtectedLayout({ children }: React.PropsWithChildren) {
119
+ return (
120
+ <NotAuthenticatedContainer fallback={<LoginRedirect />}>
121
+ {children}
122
+ </NotAuthenticatedContainer>
123
+ );
124
+ }
125
+ ```
126
+
127
+ > [!NOTE]
128
+ > If you are using `<NotAuthenticatedContainer />` in a `layout.tsx` file, and if you are using `error.tsx` file convention, you will need to wrap your `error.tsx` with `NotAuthenticatedErrorWrapper` due to Next.js layout, page, and error boundary hierarchy.
129
+ >
130
+ > ```tsx
131
+ > // app/error.tsx
132
+ > 'use client';
133
+ >
134
+ > import { NotAuthenticatedErrorWrapper } from 'sekisho';
135
+ >
136
+ > export default function ErrorPage({ error, reset }) {
137
+ > return (
138
+ > <NotAuthenticatedErrorWrapper error={error}>
139
+ > {/* Your existing error UI goes in here */}
140
+ > </NotAuthenticatedErrorWrapper>
141
+ > );
142
+ > }
143
+ > ```
144
+
145
+ **Set up with React Router**
146
+
147
+ ```tsx
148
+ import { NotAuthenticatedContainer } from 'sekisho';
149
+
150
+ const router = createBrowserRouter([
151
+ {
152
+ element: <RootLayout />,
153
+ children: [
154
+ // protected routes goes here
155
+ {
156
+ element: <Protected />,
157
+ children: [
158
+ /* protected routes goes here */
159
+ { path: '/admin', element: <Dashboard /> }
160
+ ]
161
+ },
162
+ // unprotected routes goes here
163
+ { path: '/login', element: <LoginPage /> }
164
+ ]
165
+ }
166
+ ]);
167
+
168
+ function Protected() {
169
+ return (
170
+ <NotAuthenticatedContainer fallback={<Navigate to="/login" />}>
171
+ <Outlet />
172
+ </NotAuthenticatedContainer>
173
+ );
174
+ }
175
+ ```
176
+
177
+ > [!NOTE]
178
+ > If you are using React Router's `errorElement` convention, you may need to wrap your `errorElement` with `NotAuthenticatedErrorWrapper`:
179
+ >
180
+ > ```tsx
181
+ > {
182
+ > errorElement: <ErrorComponent />
183
+ > }
184
+ >
185
+ > function ErrorComponent() {
186
+ > const error = useRouteError();
187
+ > return (
188
+ > <NotAuthenticatedErrorWrapper error={error}>
189
+ > {/* Your existing error UI goes in here */}
190
+ > </NotAuthenticatedErrorWrapper>
191
+ > );
192
+ > }
193
+ > ```
194
+
195
+ ### Restricting Access to Part of the UI
135
196
 
136
197
  Wrap any part of the UI with `AccessRestrictedContainer` and call `accessRestricted()` inside it when the user lacks the required role or permission. Unlike `needLogin()`, which triggers a global redirect via `onNeedLogin`, `accessRestricted()` is local — `AccessRestrictedContainer` simply renders `fallback` in place of its children:
137
198
 
@@ -148,10 +209,20 @@ function AdminPanel() {
148
209
  return <div>Secret admin content</div>;
149
210
  }
150
211
 
212
+ function AccessRestricted() {
213
+ // you may render a fallback UI in place of the restricted content
214
+ return <p>You don't have permission to view this section.</p>
215
+
216
+ // or you may just redirect your user away
217
+ return redirect('/forbidden');
218
+ }
219
+
151
220
  function Page() {
152
221
  return (
153
222
  <AccessRestrictedContainer
154
- fallback={<p>You don't have permission to view this section.</p>}
223
+ fallback={<AccessRestricted />}
224
+ // or you can pass a component that receives the error object as prop
225
+ fallbackComponent={AccessRestricted}
155
226
  >
156
227
  <AdminPanel />
157
228
  </AccessRestrictedContainer>
@@ -163,72 +234,30 @@ This kinda like `<Suspense />` but for access control instead. And like `<Suspen
163
234
 
164
235
  ## Explanation
165
236
 
166
- Sekisho is built on top of React's error boundaries. Both `needLogin()` and `accessRestricted()` throw a special tagged error during the React render phase, which bubbles up to the nearest matching boundary:
167
-
168
- | Function | Error thrown | Caught by | Behaviour |
169
- |---|---|---|---|
170
- | `needLogin()` | `NotAuthenticatedError` | `NotAuthenticatedBoundary` / `NotAuthenticatedErrorWrapper` | Calls `onNeedLogin` from `SekishoProvider` (global redirect) |
171
- | `accessRestricted()` | `AccessRestrictedError` | `AccessRestrictedContainer` | Renders the `fallback` prop in place of children (local swap) |
172
-
173
- Each boundary re-throws errors it does not own, so `AccessRestrictedContainer` never swallows an auth error, and `NotAuthenticatedBoundary` never swallows an access error. Your own error boundaries are unaffected by either.
174
-
175
- With `NotAuthenticatedErrorWrapper` / `NotAuthenticatedBoundary` you can create protected and unprotected routes in any React app:
176
-
177
- **Next.js App Router**
178
-
179
- ```
180
- app/
181
- ├── (protected)/ ← all protected routes goes under here
182
- │ ├── layout.tsx ← wrap children with <SekishoProvider> here
183
- │ ├── error.tsx ← wrap with <NotAuthenticatedErrorWrapper> here
184
- │ └── page.tsx ← homepage, where you call needLogin() when authentication is needed
185
- ├── (unprotected)/ ← all unprotected routes goes under here
186
- │ └── login/
187
- │ └── page.tsx
188
- └── layout.tsx ← root layout
189
- ```
190
-
191
- **React Router**
192
-
193
- ```tsx
194
- const router = createBrowserRouter([
195
- {
196
- component() {
197
- return (
198
- <RootLayout>
199
- <SekishoProvider onNeedLogin={() => navigate('/login')}>
200
- <Outlet />
201
- </SekishoProvider>
202
- </RootLayout>
203
- );
204
- },
205
- children: [
206
- {
207
- component() {
208
- return (
209
- <NotAuthenticatedBoundary>
210
- <Outlet />
211
- </NotAuthenticatedBoundary>
212
- );
213
- },
214
- children: [
215
- // protected routes goes here
216
- ]
217
- },
218
- // unprotected routes goes here
219
- ]
220
- }
221
- ]);
222
- ```
237
+ Sekisho is built on top of React's error boundaries. Both `needLogin()` and `accessRestricted()` throw a special tagged error during the React render phase, which bubbles up to the nearest matching boundary. Each boundary re-throws errors it does not own, so your own error boundaries are unaffected.
223
238
 
224
- ## Build your own guard
239
+ ## Build Your Own Custom Gate
225
240
 
226
241
  Sekisho also provides a low-level abstraction `createSekisho` from `sekisho/factory` for building your own custom gate with the same underlying mechanism. Let's say you want to build a guard for new user onboarding flow, where users need to complete their profile before accessing certain parts of the app:
227
242
 
228
243
  ```tsx
229
244
  import { createSekisho } from 'sekisho/factory';
245
+ import { redirect } from 'next/navigation';
246
+
247
+ const [requireOnboarding, OnboardingGate, OnboardingErrorWrapper] = createSekisho('OnboardingRequired');
230
248
 
231
- const [requireOnboarding, OnboardingGate] = createSekisho('OnboardingRequired');
249
+ function OnboardingRedirect(): never {
250
+ redirect('/onboarding');
251
+ }
252
+
253
+ function Protected({ children }: React.PropsWithChildren) {
254
+ return (
255
+ <OnboardingGate fallback={<OnboardingRedirect />}>
256
+ {/* actual content goes here */}
257
+ <Dashboard />
258
+ </OnboardingGate>
259
+ );
260
+ }
232
261
 
233
262
  function Dashboard() {
234
263
  const user = useUser();
@@ -237,34 +266,18 @@ function Dashboard() {
237
266
  requireOnboarding('Profile incomplete');
238
267
  }
239
268
 
240
- // actual dashboard content
241
269
  return <div>Welcome back, {user.name}</div>;
242
270
  }
243
-
244
- function OnboardingGuard({ error }) {
245
- redirect('/onboarding');
246
- }
247
-
248
- // Wrap your app with the boundary component
249
- function Page() {
250
- return (
251
- <OnboardingGate
252
- fallback={<OnboardingGuard />}
253
- // or you can pass a component that receives the error prop
254
- fallbackComponent={OnboardingGuard}
255
- >
256
- <Dashboard />
257
- </OnboardingGate>
258
- );
259
- }
260
271
  ```
261
272
 
262
- The `createSekisho()` factory returns a 4-tuple: `[throwFn, BoundaryComponent, isError, ErrorClass]`. You can name each element whatever makes sense for your use case:
273
+ The `createSekisho()` factory returns a 5-tuple so each element can be named freely on destructure:
274
+ `[throwFn, ContainerComponent, ErrorWrapper, isError, ErrorClass]`
263
275
 
264
276
  - **`throwFn`** — Call this during render to trigger the guard when a condition is unmet
265
- - **`BoundaryComponent`** — Error boundary that catches errors thrown by `throwFn`. Accepts `fallback` (static UI) or `fallbackComponent` (component that receives `{ error }`)
277
+ - **`ContainerComponent`** — Error boundary that catches errors thrown by `throwFn`. Accepts `fallback` (static UI) or `fallbackComponent` (component that receives `{ error }`). Stores the options in context for `ErrorWrapper` to reuse.
278
+ - **`ErrorWrapper`** — Companion for framework error boundaries (Next.js `error.tsx`, React Router `errorElement`, etc.). Reads `fallback`/`fallbackComponent` from the nearest `ContainerComponent` ancestor in context.
266
279
  - **`isError`** — Type guard to check if an error is from this guard (useful in middleware or error handlers)
267
- - **`ErrorClass`** — The error constructor, if you need `instanceof` checks
280
+ - **`ErrorClass`** — The error constructor
268
281
 
269
282
  Each call to `createSekisho()` is isolated — guards never accidentally catch each other's errors, even if nested.
270
283
 
package/dist/factory.cjs CHANGED
@@ -1 +1 @@
1
- "use client";Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react/jsx-runtime"),e=require("foxact/create-stackless-error"),t=require("react");exports.createSekisho=function(s){let o=new WeakSet;class n extends Error{constructor(r){super(r),this.digest="BAILOUT_TO_CLIENT_SIDE_RENDERING",this.name=s??"SekishoGuardError",o.add(this)}}function a(r){return!!r&&"object"==typeof r&&o.has(r)}class c extends t.Component{constructor(r){super(r),this.state={caughtError:null}}static getDerivedStateFromError(r){if(a(r))return{caughtError:r};throw r}render(){let{caughtError:e}=this.state;if(null!==e){let{fallback:t,fallbackComponent:s}=this.props;return s?r.jsx(s,{error:e}):t}return this.props.children}}return[function(r){throw e.createStacklessError(()=>new n(r))},c,a,n]};
1
+ "use client";Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react/jsx-runtime"),e=require("foxact/create-stackless-error"),t=require("foxact/nullthrow"),n=require("react");exports.createSekisho=function(o){let s=new WeakSet;class u extends Error{constructor(r){super(r),this.digest="BAILOUT_TO_CLIENT_SIDE_RENDERING",this.name=o??"SekishoGuardError",s.add(this)}}function i(r){return"object"==typeof r&&null!==r&&s.has(r)}let c=n.createContext(null);function l({error:e,children:o}){let s=t.nullthrow(n.useContext(c),"<ErrorWrapper /> must be used within its corresponding container component");if(i(e)){let{fallback:t,fallbackComponent:n}=s;return n?r.jsx(n,{error:e}):t}return o}class a extends n.Component{constructor(r){super(r),this.state={error:null}}static getDerivedStateFromError(r){return{error:r}}render(){let e=this.state.error;if(null===e)return this.props.children;if(i(e))return r.jsx(l,{error:e});throw e}}return[function(r){throw e.createStacklessError(()=>new u(r))},function({children:e,...t}){return r.jsx(c.Provider,{value:t,children:r.jsx(a,{children:e})})},l,i,u]};
package/dist/factory.d.ts CHANGED
@@ -3,7 +3,7 @@ interface SekishoGuardError extends Error {
3
3
  readonly digest: 'BAILOUT_TO_CLIENT_SIDE_RENDERING';
4
4
  }
5
5
  /**
6
- * Props accepted by the boundary component returned from `createSekisho`.
6
+ * Props accepted by the container component returned from `createSekisho`.
7
7
  *
8
8
  * Exactly one of `fallback` or `fallbackComponent` must be provided:
9
9
  *
@@ -13,7 +13,7 @@ interface SekishoGuardError extends Error {
13
13
  * Use this when you need the caught error object, e.g. to trigger a
14
14
  * navigation side-effect (auth pattern).
15
15
  */
16
- type SekishoGuardBoundaryProps = React.PropsWithChildren & ({
16
+ type SekishoContainerProps = React.PropsWithChildren & ({
17
17
  fallback: React.ReactNode;
18
18
  fallbackComponent?: never;
19
19
  } | {
@@ -22,21 +22,32 @@ type SekishoGuardBoundaryProps = React.PropsWithChildren & ({
22
22
  error: SekishoGuardError;
23
23
  }>;
24
24
  });
25
- interface SekishoGuardBoundaryState {
26
- caughtError: SekishoGuardError | null;
25
+ /**
26
+ * Props accepted by the `ErrorWrapper` component returned from `createSekisho`.
27
+ *
28
+ * Mirrors the shape of framework error boundary props (Next.js `error.tsx`,
29
+ * React Router `errorElement`, etc.) so the component can be used as a direct
30
+ * wrapper without any adapter layer.
31
+ */
32
+ interface SekishoErrorWrapperProps extends React.PropsWithChildren {
33
+ error: unknown;
27
34
  }
28
35
  /**
29
- * Creates a paired guard throw function, error boundary component, type guard,
30
- * and error class — all isolated from every other guard in the tree.
36
+ * Creates a paired guard throw function, container component, error wrapper,
37
+ * type guard, and error class — all isolated from every other guard in the tree.
31
38
  *
32
39
  * Call the returned throw function anywhere in the React render phase to signal
33
- * that a condition is unmet. The nearest boundary component in the tree will
40
+ * that a condition is unmet. The nearest container component in the tree will
34
41
  * catch it and render its `fallback` prop instead of `children`. Every other
35
42
  * error boundary — including ones from other `createSekisho()` calls —
36
43
  * re-throws the error unchanged.
37
44
  *
38
- * Returns a 4-tuple so each element can be named freely on destructure:
39
- * `[throwFn, BoundaryComponent, isError, ErrorClass]`
45
+ * The container component stores its `fallback`/`fallbackComponent` in context so
46
+ * that `ErrorWrapper` can reuse it from a framework error boundary (e.g. Next.js
47
+ * `error.tsx` or React Router `errorElement`) without repeating the redirect logic.
48
+ *
49
+ * Returns a 5-tuple so each element can be named freely on destructure:
50
+ * `[throwFn, ContainerComponent, ErrorWrapper, isError, ErrorClass]`
40
51
  *
41
52
  * @example
42
53
  * // Access-control pattern — static fallback element:
@@ -47,19 +58,34 @@ interface SekishoGuardBoundaryState {
47
58
  * </OnboardingGate>
48
59
  *
49
60
  * @example
50
- * // Callback pattern — component receives the error object:
51
- * const [requireAuth, AuthGate] = createSekisho();
61
+ * // Auth pattern — render-phase redirect:
62
+ * const [requireAuth, AuthGate, AuthErrorWrapper] = createSekisho();
63
+ *
64
+ * function LoginRedirect(): never { redirect('/login'); }
65
+ *
66
+ * // In layout:
67
+ * <AuthGate fallbackComponent={LoginRedirect}>{children}</AuthGate>
68
+ *
69
+ * // In Next.js error.tsx:
70
+ * export default function ErrorPage({ error, reset }) {
71
+ * return <AuthErrorWrapper error={error}>...</AuthErrorWrapper>;
72
+ * }
73
+ *
74
+ * // In React Router errorElement:
75
+ * const ErrorComponent = () => {
76
+ * const error = useRouteError();
77
+ * return <AuthErrorWrapper error={error}>...</AuthErrorWrapper>;
78
+ * }
52
79
  *
53
- * <AuthGate fallbackComponent={AuthErrorHandler}>
54
- * <Dashboard />
55
- * </AuthGate>
80
+ * { errorElement: <ErrorComponent /> }
56
81
  */
57
82
  declare function createSekisho(errorName?: string): [
58
83
  throwError: (message: string) => never,
59
- BoundaryComponent: React.ComponentClass<SekishoGuardBoundaryProps, SekishoGuardBoundaryState>,
84
+ ContainerComponent: (props: SekishoContainerProps) => React.ReactNode,
85
+ ErrorWrapper: (props: SekishoErrorWrapperProps) => React.ReactNode,
60
86
  isError: (error: unknown) => error is SekishoGuardError,
61
87
  ErrorClass: new (message: string) => SekishoGuardError
62
88
  ];
63
89
 
64
90
  export { createSekisho };
65
- export type { SekishoGuardBoundaryProps, SekishoGuardBoundaryState, SekishoGuardError };
91
+ export type { SekishoContainerProps, SekishoErrorWrapperProps, SekishoGuardError };
package/dist/factory.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import{jsx as r}from"react/jsx-runtime";import{createStacklessError as t}from"foxact/create-stackless-error";import{Component as e}from"react";function o(o){let s=new WeakSet;class n extends Error{constructor(r){super(r),this.digest="BAILOUT_TO_CLIENT_SIDE_RENDERING",this.name=o??"SekishoGuardError",s.add(this)}}function i(r){return!!r&&"object"==typeof r&&s.has(r)}return[function(r){throw t(()=>new n(r))},class extends e{constructor(r){super(r),this.state={caughtError:null}}static getDerivedStateFromError(r){if(i(r))return{caughtError:r};throw r}render(){let{caughtError:t}=this.state;if(null!==t){let{fallback:e,fallbackComponent:o}=this.props;return o?r(o,{error:t}):e}return this.props.children}},i,n]}export{o as createSekisho};
1
+ "use client";import{jsx as r}from"react/jsx-runtime";import{createStacklessError as t}from"foxact/create-stackless-error";import{nullthrow as e}from"foxact/nullthrow";import{useContext as o,createContext as n,Component as i}from"react";function s(s){let c=new WeakSet;class u extends Error{constructor(r){super(r),this.digest="BAILOUT_TO_CLIENT_SIDE_RENDERING",this.name=s??"SekishoGuardError",c.add(this)}}function l(r){return"object"==typeof r&&null!==r&&c.has(r)}let a=n(null);function f({error:t,children:n}){let i=e(o(a),"<ErrorWrapper /> must be used within its corresponding container component");if(l(t)){let{fallback:e,fallbackComponent:o}=i;return o?r(o,{error:t}):e}return n}class h extends i{constructor(r){super(r),this.state={error:null}}static getDerivedStateFromError(r){return{error:r}}render(){let t=this.state.error;if(null===t)return this.props.children;if(l(t))return r(f,{error:t});throw t}}return[function(r){throw t(()=>new u(r))},function({children:t,...e}){return r(a.Provider,{value:e,children:r(h,{children:t})})},f,l,u]}export{s as createSekisho};
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use client";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),r=require("foxact/use-isomorphic-layout-effect"),t=require("foxact/use-stable-handler-only-when-you-know-what-you-are-doing-or-you-will-be-fired"),o=require("./factory.cjs"),s=require("foxact/nullthrow"),i=require("react");let n=i.createContext(null),[c,u,a,l]=o.createSekisho("NotAuthenticatedError");function d({error:e,children:o}){let{onNeedLogin:c}=s.nullthrow(i.useContext(n),"useSekishoOptions must be used within a SekishoOptionsProvider"),u=a(e),l=t.useStableHandler(c);return(r.useLayoutEffect(()=>{u&&l()},[u,l]),u)?null:o}function p({children:r}){return e.jsx(u,{fallbackComponent:d,children:r})}let[h,x,f,k]=o.createSekisho("AccessRestrictedError");Object.defineProperty(exports,"createSekisho",{enumerable:!0,get:function(){return o.createSekisho}}),exports.AccessRestrictedContainer=x,exports.AccessRestrictedError=k,exports.NotAuthenticatedBoundary=p,exports.NotAuthenticatedError=l,exports.NotAuthenticatedErrorWrapper=d,exports.SekishoAccessContainer=x,exports.SekishoErrorBoundary=p,exports.SekishoErrorWrapper=d,exports.SekishoProvider=function({children:r,...t}){return e.jsx(n.Provider,{value:t,children:e.jsx(p,{children:r})})},exports.accessRestricted=h,exports.isAccessRestrictedError=f,exports.isNeedLoginError=a,exports.needLogin=c;
1
+ "use client";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("./factory.cjs");let[r,t,s,o,c]=e.createSekisho("NotAuthenticatedError"),[i,p,a,d,n]=e.createSekisho("AccessRestrictedError");exports.AccessRestrictedContainer=p,exports.AccessRestrictedError=n,exports.AccessRestrictedErrorWrapper=a,exports.NotAuthenticatedContainer=t,exports.NotAuthenticatedError=c,exports.NotAuthenticatedErrorWrapper=s,exports.accessRestricted=i,exports.isAccessRestrictedError=d,exports.isNeedLoginError=o,exports.needLogin=r;
package/dist/index.d.ts CHANGED
@@ -1,57 +1,21 @@
1
- import * as react from 'react';
2
1
  import * as __factory from './factory.js';
3
- import { SekishoGuardBoundaryProps } from './factory.js';
4
- export { SekishoGuardBoundaryProps, SekishoGuardBoundaryState, SekishoGuardError, createSekisho } from './factory.js';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { SekishoContainerProps, SekishoErrorWrapperProps } from './factory.js';
6
3
 
7
4
  declare const needLogin: (message: string) => never;
5
+ declare const NotAuthenticatedContainer: (props: SekishoContainerProps) => React.ReactNode;
6
+ declare const NotAuthenticatedErrorWrapper: (props: SekishoErrorWrapperProps) => React.ReactNode;
8
7
  declare const isNeedLoginError: (error: unknown) => error is __factory.SekishoGuardError;
9
8
  declare const NotAuthenticatedError: new (message: string) => __factory.SekishoGuardError;
10
-
11
- interface NotAuthenticatedErrorWrapperProps extends React.PropsWithChildren {
12
- error: unknown | null | undefined;
13
- }
14
- /** @deprecated `SekishoErrorWrapperProps` has since been renamed to `NotAuthenticatedErrorWrapperProps` */
15
- type SekishoErrorWrapperProps = NotAuthenticatedErrorWrapperProps;
16
- /**
17
- * The actual error handling and redirection logic for "Not Authenticated" error.
18
- *
19
- * Used internally by `NotAuthenticatedBoundary`. You can also use this directly in
20
- * a Next.js `app/error.tsx` file for custom error handling.
21
- */
22
- declare function NotAuthenticatedErrorWrapper({ error, children }: NotAuthenticatedErrorWrapperProps): react.ReactNode;
23
-
24
- interface NotAuthenticatedBoundaryProps extends React.PropsWithChildren {
25
- }
26
- /**
27
- * Error boundary that catches `NotAuthenticatedError` thrown by `needLogin()`
28
- * within its subtree and calls the `onNeedLogin` callback from `SekishoProvider`.
29
- * All other errors are re-thrown to the next boundary up the tree.
30
- *
31
- * This is included inside `SekishoProvider` automatically; you only need to use
32
- * it directly if you want a narrower boundary for a specific subtree.
33
- */
34
- declare function NotAuthenticatedBoundary({ children }: NotAuthenticatedBoundaryProps): React.ReactNode;
35
- /** @deprecated `SekishoErrorBoundaryProps` has since been renamed to `NotAuthenticatedBoundaryProps` */
36
- type SekishoErrorBoundaryProps = NotAuthenticatedBoundaryProps;
37
-
38
- interface SekishoOptions {
39
- onNeedLogin: () => void;
40
- }
41
-
42
- interface SekishoProviderProps extends React.PropsWithChildren, SekishoOptions {
43
- }
44
- declare function SekishoProvider({ children, ...auth }: SekishoProviderProps): react_jsx_runtime.JSX.Element;
9
+ type NotAuthenticatedContainerProps = SekishoContainerProps;
10
+ type NotAuthenticatedErrorWrapperProps = SekishoErrorWrapperProps;
45
11
 
46
12
  declare const accessRestricted: (message: string) => never;
47
- declare const AccessRestrictedContainer: react.ComponentClass<SekishoGuardBoundaryProps, __factory.SekishoGuardBoundaryState>;
13
+ declare const AccessRestrictedContainer: (props: SekishoContainerProps) => React.ReactNode;
14
+ declare const AccessRestrictedErrorWrapper: (props: SekishoErrorWrapperProps) => React.ReactNode;
48
15
  declare const isAccessRestrictedError: (error: unknown) => error is __factory.SekishoGuardError;
49
16
  declare const AccessRestrictedError: new (message: string) => __factory.SekishoGuardError;
50
- type AccessRestrictedContainerProps = SekishoGuardBoundaryProps;
51
- /** @deprecated `SekishoAccessContainer` has since been renamed to `AccessRestrictedContainer` */
52
- declare const SekishoAccessContainer: react.ComponentClass<SekishoGuardBoundaryProps, __factory.SekishoGuardBoundaryState>;
53
- /** @deprecated `SekishoAccessContainerProps` has since been renamed to `AccessRestrictedContainerProps` */
54
- type SekishoAccessContainerProps = AccessRestrictedContainerProps;
17
+ type AccessRestrictedContainerProps = SekishoContainerProps;
18
+ type AccessRestrictedErrorWrapperProps = SekishoErrorWrapperProps;
55
19
 
56
- export { AccessRestrictedContainer, AccessRestrictedError, NotAuthenticatedBoundary, NotAuthenticatedError, NotAuthenticatedErrorWrapper, SekishoAccessContainer, NotAuthenticatedBoundary as SekishoErrorBoundary, NotAuthenticatedErrorWrapper as SekishoErrorWrapper, SekishoProvider, accessRestricted, isAccessRestrictedError, isNeedLoginError, needLogin };
57
- export type { AccessRestrictedContainerProps, NotAuthenticatedBoundaryProps, NotAuthenticatedErrorWrapperProps, SekishoAccessContainerProps, SekishoErrorBoundaryProps, SekishoErrorWrapperProps, SekishoOptions, SekishoProviderProps };
20
+ export { AccessRestrictedContainer, AccessRestrictedError, AccessRestrictedErrorWrapper, NotAuthenticatedContainer, NotAuthenticatedError, NotAuthenticatedErrorWrapper, accessRestricted, isAccessRestrictedError, isNeedLoginError, needLogin };
21
+ export type { AccessRestrictedContainerProps, AccessRestrictedErrorWrapperProps, NotAuthenticatedContainerProps, NotAuthenticatedErrorWrapperProps };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import{jsx as r}from"react/jsx-runtime";import{useLayoutEffect as e}from"foxact/use-isomorphic-layout-effect";import{useStableHandler as t}from"foxact/use-stable-handler-only-when-you-know-what-you-are-doing-or-you-will-be-fired";import{createSekisho as o}from"./factory.mjs";export{createSekisho}from"./factory.mjs";import{nullthrow as i}from"foxact/nullthrow";import{createContext as n,useContext as c}from"react";let s=n(null),[a,u,d,h]=o("NotAuthenticatedError");function l({error:r,children:o}){let{onNeedLogin:n}=i(c(s),"useSekishoOptions must be used within a SekishoOptionsProvider"),a=d(r),u=t(n);return(e(()=>{a&&u()},[a,u]),a)?null:o}function f({children:e}){return r(u,{fallbackComponent:l,children:e})}function m({children:e,...t}){return r(s.Provider,{value:t,children:r(f,{children:e})})}let[p,A,y,E]=o("AccessRestrictedError"),k=A;export{A as AccessRestrictedContainer,E as AccessRestrictedError,f as NotAuthenticatedBoundary,h as NotAuthenticatedError,l as NotAuthenticatedErrorWrapper,k as SekishoAccessContainer,f as SekishoErrorBoundary,l as SekishoErrorWrapper,m as SekishoProvider,p as accessRestricted,y as isAccessRestrictedError,d as isNeedLoginError,a as needLogin};
1
+ "use client";import{createSekisho as r}from"./factory.mjs";let[e,t,c,s,o]=r("NotAuthenticatedError"),[i,d,n,a,A]=r("AccessRestrictedError");export{d as AccessRestrictedContainer,A as AccessRestrictedError,n as AccessRestrictedErrorWrapper,t as NotAuthenticatedContainer,o as NotAuthenticatedError,c as NotAuthenticatedErrorWrapper,i as accessRestricted,a as isAccessRestrictedError,s as isNeedLoginError,e as needLogin};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sekisho",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Authentication and Access Control for any React app",
5
5
  "repository": {
6
6
  "type": "git",