sekisho 0.1.0 → 0.1.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.
Files changed (2) hide show
  1. package/README.md +204 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ <h1 align="center">⛩️ sekisho</h1>
2
+ <p align="center"><sup>(関所, <em>historical checkpoint for travel and security</em> in Japanese)</sup></p>
3
+ <p align="center">Authentication and Access Control for any React app</p>
4
+
5
+ ----
6
+
7
+ ## Usage
8
+
9
+ ### Full example
10
+
11
+ See the [example-nextjs-app](../../packages/example-nextjs-app).
12
+
13
+ ### Simple setup
14
+
15
+ Wrap your app with `SekishoProvider` with options:
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
+ ```
48
+
49
+ ```tsx
50
+ // app/layout.tsx
51
+ import { Providers } from './providers';
52
+
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
+ ```
63
+
64
+ By default, `SekishoProvider` already includes `SekishoErrorBoundary` 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 `SekishoErrorBoundary` directly to wrap those parts:
65
+
66
+ ```tsx
67
+ import { SekishoErrorBoundary } from 'sekisho';
68
+
69
+ function SomePartOfApp() {
70
+ return (
71
+ <SekishoErrorBoundary>
72
+ {/* ... */}
73
+ </SekishoErrorBoundary>
74
+ );
75
+ }
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 `SekishoErrorWrapper`:
79
+
80
+ ```tsx
81
+ // app/error.tsx
82
+ 'use client';
83
+
84
+ import { SekishoErrorWrapper } from 'sekisho';
85
+
86
+ export default function ErrorPage({ error, reset }) {
87
+ return (
88
+ <SekishoErrorWrapper error={error}>
89
+ {/* Your existing error UI goes in here */}
90
+ </SekishoErrorWrapper>
91
+ );
92
+ }
93
+ ```
94
+
95
+ > `SekishoErrorWrapper` is actually used by `SekishoErrorBoundary` internally, containing all the core logic.
96
+
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):
104
+
105
+ ```tsx
106
+ import { needLogin } from 'sekisho';
107
+
108
+ function Dashboard() {
109
+ const session = useAuthSession();
110
+
111
+ if (!session) {
112
+ needLogin('No active session');
113
+ }
114
+
115
+ return <div>Welcome, {session.username}</div>;
116
+ }
117
+ ```
118
+
119
+ **In a [SWR](https://swr.vercel.app) middleware** (e.g. when an API response carries a known auth error):
120
+
121
+ ```tsx
122
+ import { needLogin } from 'sekisho';
123
+ import type { Middleware } from 'swr';
124
+
125
+ export const requireAuthMiddleware: Middleware = (useSWRNext) => (key, fetcher, config) => {
126
+ const swr = useSWRNext(key, fetcher, config);
127
+ if (swr.error && isApiAuthError(swr.error)) {
128
+ needLogin(swr.error.message);
129
+ }
130
+ return swr;
131
+ };
132
+ ```
133
+
134
+ ## Explanation
135
+
136
+ Sekisho is built on top of React's error boundaries. When you call `needLogin()` within the React render phase, it throws a special `NotAuthenticatedError`. React will then bubble the error up to the nearest React error boundary, and that's when Sekisho's error boundary went into action: it checks if the error is a `NotAuthenticatedError`, and if so, it calls the `onNeedLogin` callback you provided to `SekishoProvider`, and re-throws the error if it is not.
137
+
138
+ This way, you can centralize your authentication logic and keep it separate from your UI components.
139
+
140
+ Also, with the `SekishoErrorWrapper` / `SekishoErrorBoundary`, you can create protected routes and unprotected routes more easily with any React apps:
141
+
142
+ **Next.js App Router**
143
+
144
+ ```
145
+ app/
146
+ ├── (protected)/ ← all protected routes goes under here
147
+ │ ├── layout.tsx ← wrap children with <SekishoProvider> here
148
+ │ ├── error.tsx ← wrap with <SekishoErrorWrapper> here
149
+ │ └── page.tsx ← homepage, where you call needLogin() when authentication is needed
150
+ ├── (unprotected)/ ← all unprotected routes goes under here
151
+ │ └── login/
152
+ │ └── page.tsx
153
+ └── layout.tsx ← root layout
154
+ ```
155
+
156
+ **React Router**
157
+
158
+ ```tsx
159
+ const router = createBrowserRouter([
160
+ {
161
+ component() {
162
+ return (
163
+ <RootLayout>
164
+ <SekishoProvider onNeedLogin={() => navigate('/login')}>
165
+ <Outlet />
166
+ </SekishoProvider>
167
+ </RootLayout>
168
+ );
169
+ },
170
+ children: [
171
+ {
172
+ component() {
173
+ return (
174
+ <SekishoErrorBoundary>
175
+ <Outlet />
176
+ </SekishoErrorBoundary>
177
+ );
178
+ },
179
+ children: [
180
+ // protected routes goes here
181
+ ]
182
+ },
183
+ // unprotected routes goes here
184
+ ]
185
+ }
186
+ ]);
187
+ ```
188
+
189
+ ## License
190
+
191
+ [MIT](LICENSE)
192
+
193
+ ----
194
+
195
+ **sekisho** © [Sukka](https://github.com/SukkaW), Released under the [MIT](./LICENSE) License.
196
+ Authored and maintained by Sukka with help from contributors ([list](https://github.com/SukkaW/sekisho/graphs/contributors)).
197
+
198
+ > [Personal Website](https://skk.moe) · [Blog](https://blog.skk.moe) · GitHub [@SukkaW](https://github.com/SukkaW) · Telegram Channel [@SukkaChannel](https://t.me/SukkaChannel) · Mastodon [@sukka@acg.mn](https://acg.mn/@sukka) · Twitter [@isukkaw](https://twitter.com/isukkaw) · BlueSky [@skk.moe](https://bsky.app/profile/skk.moe)
199
+
200
+ <p align="center">
201
+ <a href="https://github.com/sponsors/SukkaW/">
202
+ <img src="https://sponsor.cdn.skk.moe/sponsors.svg"/>
203
+ </a>
204
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sekisho",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Authentication and Access Control for any React app",
5
5
  "repository": {
6
6
  "type": "git",