@hono/auth-js 1.0.11 → 1.0.12

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/react.js CHANGED
@@ -41,11 +41,11 @@ __export(react_exports, {
41
41
  useSession: () => useSession
42
42
  });
43
43
  module.exports = __toCommonJS(react_exports);
44
- var React2 = __toESM(require("react"));
44
+ var React = __toESM(require("react"));
45
45
 
46
46
  // src/client.ts
47
47
  var import_errors = require("@auth/core/errors");
48
- var React = __toESM(require("react"));
48
+ var import_react = require("react");
49
49
  var ClientFetchError = class extends import_errors.AuthError {
50
50
  };
51
51
  var ClientSessionError = class extends import_errors.AuthError {
@@ -76,17 +76,18 @@ async function fetchData(path, config, logger2, req = {}) {
76
76
  }
77
77
  }
78
78
  function useOnline() {
79
- const [isOnline, setIsOnline] = React.useState(
79
+ const [isOnline, setIsOnline] = (0, import_react.useState)(
80
80
  typeof navigator !== "undefined" ? navigator.onLine : false
81
81
  );
82
- React.useEffect(() => {
82
+ (0, import_react.useEffect)(() => {
83
+ const abortController = new AbortController();
84
+ const { signal } = abortController;
83
85
  const setOnline = () => setIsOnline(true);
84
86
  const setOffline = () => setIsOnline(false);
85
- window.addEventListener("online", setOnline);
86
- window.addEventListener("offline", setOffline);
87
+ window.addEventListener("online", setOnline, { signal });
88
+ window.addEventListener("offline", setOffline, { signal });
87
89
  return () => {
88
- window.removeEventListener("online", setOnline);
89
- window.removeEventListener("offline", setOffline);
90
+ abortController.abort();
90
91
  };
91
92
  }, []);
92
93
  return isOnline;
@@ -109,17 +110,28 @@ function parseUrl(url) {
109
110
  }
110
111
 
111
112
  // src/react.tsx
113
+ var import_react2 = require("react");
114
+ var logger = {
115
+ debug: console.debug,
116
+ error: console.error,
117
+ warn: console.warn
118
+ };
112
119
  var AuthConfigManager = class _AuthConfigManager {
113
120
  static instance = null;
114
- _config = {
115
- baseUrl: typeof window !== "undefined" ? parseUrl(window.location.origin).origin : "",
116
- basePath: typeof window !== "undefined" ? parseUrl(window.location.origin).path : "/api/auth",
117
- credentials: "same-origin",
118
- _lastSync: 0,
119
- _session: void 0,
120
- _getSession: () => {
121
- }
122
- };
121
+ config;
122
+ constructor() {
123
+ this.config = this.createDefaultConfig();
124
+ }
125
+ createDefaultConfig() {
126
+ return {
127
+ baseUrl: typeof window !== "undefined" ? parseUrl(window.location.origin).origin : "",
128
+ basePath: typeof window !== "undefined" ? parseUrl(window.location.origin).path : "/api/auth",
129
+ credentials: "same-origin",
130
+ lastSync: 0,
131
+ session: null,
132
+ fetchSession: async () => void 0
133
+ };
134
+ }
123
135
  static getInstance() {
124
136
  if (!_AuthConfigManager.instance) {
125
137
  _AuthConfigManager.instance = new _AuthConfigManager();
@@ -127,43 +139,158 @@ var AuthConfigManager = class _AuthConfigManager {
127
139
  return _AuthConfigManager.instance;
128
140
  }
129
141
  setConfig(userConfig) {
130
- this._config = { ...this._config, ...userConfig };
142
+ this.config = { ...this.config, ...userConfig };
131
143
  }
132
144
  getConfig() {
133
- return this._config;
145
+ return this.config;
146
+ }
147
+ initializeConfig(hasInitialSession) {
148
+ this.config.lastSync = hasInitialSession ? now() : 0;
134
149
  }
135
150
  };
136
151
  var authConfigManager = AuthConfigManager.getInstance();
137
- function broadcast() {
138
- if (typeof BroadcastChannel !== "undefined") {
139
- return new BroadcastChannel("auth-js");
140
- }
141
- return {
142
- postMessage: () => {
152
+ var SessionContext = React.createContext(void 0);
153
+ function useInitializeSession(hasInitialSession, initialSession) {
154
+ const authConfig = authConfigManager.getConfig();
155
+ const [session, setSession] = React.useState(initialSession);
156
+ const [loading, setLoading] = React.useState(!hasInitialSession);
157
+ (0, import_react2.useEffect)(() => {
158
+ authConfig.fetchSession = async ({ event } = {}) => {
159
+ try {
160
+ const isStorageEvent = event === "storage";
161
+ if (isStorageEvent || !authConfig.session) {
162
+ authConfig.lastSync = now();
163
+ authConfig.session = await getSession();
164
+ setSession(authConfig.session);
165
+ return;
166
+ }
167
+ if (!event || !authConfig.session || now() < authConfig.lastSync) {
168
+ return;
169
+ }
170
+ authConfig.lastSync = now();
171
+ authConfig.session = await getSession();
172
+ setSession(authConfig.session);
173
+ } catch (error) {
174
+ logger.error(new ClientSessionError(error.message, error));
175
+ } finally {
176
+ setLoading(false);
177
+ }
178
+ };
179
+ authConfig.fetchSession();
180
+ return () => {
181
+ authConfig.lastSync = 0;
182
+ authConfig.session = null;
183
+ authConfig.fetchSession = async () => void 0;
184
+ };
185
+ }, []);
186
+ return { session, setSession, loading, setLoading };
187
+ }
188
+ function useVisibilityChangeEventListener(authConfig, refetchOnWindowFocus) {
189
+ (0, import_react2.useEffect)(() => {
190
+ const abortController = new AbortController();
191
+ const handleVisibilityChange = () => {
192
+ if (refetchOnWindowFocus && document.visibilityState === "visible") {
193
+ authConfig.fetchSession({ event: "visibilitychange" });
194
+ }
195
+ };
196
+ document.addEventListener("visibilitychange", handleVisibilityChange, {
197
+ signal: abortController.signal
198
+ });
199
+ return () => abortController.abort();
200
+ }, [refetchOnWindowFocus]);
201
+ }
202
+ function useRefetchInterval(authConfig, refetchInterval, shouldRefetch) {
203
+ (0, import_react2.useEffect)(() => {
204
+ if (refetchInterval && shouldRefetch) {
205
+ const intervalId = setInterval(() => {
206
+ if (authConfig.session) {
207
+ authConfig.fetchSession({ event: "poll" });
208
+ }
209
+ }, refetchInterval * 1e3);
210
+ return () => clearInterval(intervalId);
211
+ }
212
+ }, [refetchInterval, shouldRefetch]);
213
+ }
214
+ async function getSession(params) {
215
+ const { baseUrl, basePath, credentials } = authConfigManager.getConfig();
216
+ const session = await fetchData(
217
+ "session",
218
+ {
219
+ baseUrl,
220
+ basePath,
221
+ credentials
143
222
  },
144
- addEventListener: () => {
223
+ logger,
224
+ params
225
+ );
226
+ return session;
227
+ }
228
+ async function getCsrfToken() {
229
+ const { baseUrl, basePath, credentials } = authConfigManager.getConfig();
230
+ const response = await fetchData(
231
+ "csrf",
232
+ {
233
+ baseUrl,
234
+ basePath,
235
+ credentials
145
236
  },
146
- removeEventListener: () => {
147
- }
148
- };
237
+ logger
238
+ );
239
+ return response?.csrfToken ?? "";
240
+ }
241
+ function SessionProvider(props) {
242
+ if (!SessionContext) {
243
+ throw new Error("React Context is unavailable in Server Components");
244
+ }
245
+ const { children, refetchInterval, refetchWhenOffline = true } = props;
246
+ const authConfig = authConfigManager.getConfig();
247
+ const hasInitialSession = !!props.session;
248
+ authConfigManager.initializeConfig(hasInitialSession);
249
+ const { session, setSession, loading, setLoading } = useInitializeSession(
250
+ hasInitialSession,
251
+ props.session ?? null
252
+ );
253
+ useVisibilityChangeEventListener(authConfig, props.refetchOnWindowFocus ?? true);
254
+ const isOnline = useOnline();
255
+ const shouldRefetch = refetchWhenOffline || isOnline;
256
+ useRefetchInterval(authConfig, refetchInterval, shouldRefetch);
257
+ const contextValue = (0, import_react2.useMemo)(
258
+ () => ({
259
+ data: session,
260
+ status: loading ? "loading" : session ? "authenticated" : "unauthenticated",
261
+ update: async (data) => {
262
+ if (loading || !session) {
263
+ return;
264
+ }
265
+ setLoading(true);
266
+ const updatedSession = await fetchData(
267
+ "session",
268
+ authConfig,
269
+ logger,
270
+ data ? { body: { csrfToken: await getCsrfToken(), data } } : void 0
271
+ );
272
+ setLoading(false);
273
+ if (updatedSession) {
274
+ setSession(updatedSession);
275
+ }
276
+ return updatedSession;
277
+ }
278
+ }),
279
+ [session, loading, setSession]
280
+ );
281
+ return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value: contextValue }, children);
149
282
  }
150
- var logger = {
151
- debug: console.debug,
152
- error: console.error,
153
- warn: console.warn
154
- };
155
- var SessionContext = React2.createContext?.(void 0);
156
283
  function useSession(options) {
157
284
  if (!SessionContext) {
158
285
  throw new Error("React Context is unavailable in Server Components");
159
286
  }
160
- const __AUTHJS = authConfigManager.getConfig();
161
- const value = React2.useContext(SessionContext);
287
+ const config = authConfigManager.getConfig();
288
+ const session = (0, import_react2.useContext)(SessionContext);
162
289
  const { required, onUnauthenticated } = options ?? {};
163
- const requiredAndNotLoading = required && value.status === "unauthenticated";
164
- React2.useEffect(() => {
290
+ const requiredAndNotLoading = required && session?.status === "unauthenticated";
291
+ (0, import_react2.useEffect)(() => {
165
292
  if (requiredAndNotLoading) {
166
- const url = `${__AUTHJS.baseUrl}${__AUTHJS.basePath}/signin?${new URLSearchParams({
293
+ const url = `${config.baseUrl}${config.basePath}/signin?${new URLSearchParams({
167
294
  error: "SessionRequired",
168
295
  callbackUrl: window.location.href
169
296
  })}`;
@@ -176,66 +303,44 @@ function useSession(options) {
176
303
  }, [requiredAndNotLoading, onUnauthenticated]);
177
304
  if (requiredAndNotLoading) {
178
305
  return {
179
- data: value.data,
180
- update: value.update,
306
+ data: session?.data,
307
+ update: session?.update,
181
308
  status: "loading"
182
309
  };
183
310
  }
184
- return value;
185
- }
186
- async function getSession(params) {
187
- const session = await fetchData("session", authConfigManager.getConfig(), logger, params);
188
- if (params?.broadcast ?? true) {
189
- broadcast().postMessage({
190
- event: "session",
191
- data: { trigger: "getSession" }
192
- });
193
- }
194
311
  return session;
195
312
  }
196
- async function getCsrfToken() {
197
- const response = await fetchData(
198
- "csrf",
199
- authConfigManager.getConfig(),
200
- logger
201
- );
202
- return response?.csrfToken ?? "";
203
- }
204
313
  async function getProviders() {
205
314
  return fetchData("providers", authConfigManager.getConfig(), logger);
206
315
  }
207
- async function signIn(provider, options, authorizationParams) {
208
- const { callbackUrl = window.location.href, redirect = true } = options ?? {};
209
- const __AUTHJS = authConfigManager.getConfig();
210
- const href = `${__AUTHJS.baseUrl}${__AUTHJS.basePath}`;
316
+ async function signIn(provider, options = {}, authorizationParams = {}) {
317
+ const { callbackUrl = window.location.href, redirect = true, ...opts } = options;
318
+ const config = authConfigManager.getConfig();
319
+ const href = `${config.baseUrl}${config.basePath}`;
211
320
  const providers = await getProviders();
212
321
  if (!providers) {
213
322
  window.location.href = `${href}/error`;
214
323
  return;
215
324
  }
216
325
  if (!provider || !(provider in providers)) {
217
- window.location.href = `${href}/signin?${new URLSearchParams({
218
- callbackUrl
219
- })}`;
326
+ window.location.href = `${href}/signin?${new URLSearchParams({ callbackUrl })}`;
220
327
  return;
221
328
  }
222
329
  const isCredentials = providers[provider].type === "credentials";
223
330
  const isEmail = providers[provider].type === "email";
224
- const isSupportingReturn = isCredentials || isEmail;
225
331
  const signInUrl = `${href}/${isCredentials ? "callback" : "signin"}/${provider}`;
226
332
  const csrfToken = await getCsrfToken();
227
333
  const res = await fetch(`${signInUrl}?${new URLSearchParams(authorizationParams)}`, {
228
- method: "post",
334
+ method: "POST",
229
335
  headers: {
230
336
  "Content-Type": "application/x-www-form-urlencoded",
231
337
  "X-Auth-Return-Redirect": "1"
232
338
  },
233
- // @ts-expect-error TODO: Fix this
234
- body: new URLSearchParams({ ...options, csrfToken, callbackUrl }),
235
- credentials: __AUTHJS.credentials
339
+ body: new URLSearchParams({ ...opts, csrfToken, callbackUrl }),
340
+ credentials: config.credentials
236
341
  });
237
342
  const data = await res.json();
238
- if (redirect || !isSupportingReturn) {
343
+ if (redirect) {
239
344
  const url = data.url ?? callbackUrl;
240
345
  window.location.href = url;
241
346
  if (url.includes("#")) {
@@ -245,7 +350,7 @@ async function signIn(provider, options, authorizationParams) {
245
350
  }
246
351
  const error = new URL(data.url).searchParams.get("error");
247
352
  if (res.ok) {
248
- await __AUTHJS._getSession({ event: "storage" });
353
+ await config.fetchSession?.({ event: "storage" });
249
354
  }
250
355
  return {
251
356
  error,
@@ -255,146 +360,30 @@ async function signIn(provider, options, authorizationParams) {
255
360
  };
256
361
  }
257
362
  async function signOut(options) {
258
- const { callbackUrl = window.location.href } = options ?? {};
259
- const __AUTHJS = authConfigManager.getConfig();
260
- const href = `${__AUTHJS.baseUrl}${__AUTHJS.basePath}`;
363
+ const { callbackUrl = window.location.href, redirect = true } = options ?? {};
364
+ const config = authConfigManager.getConfig();
261
365
  const csrfToken = await getCsrfToken();
262
- const res = await fetch(`${href}/signout`, {
263
- method: "post",
366
+ const res = await fetch(`${config.baseUrl}${config.basePath}/signout`, {
367
+ method: "POST",
264
368
  headers: {
265
369
  "Content-Type": "application/x-www-form-urlencoded",
266
370
  "X-Auth-Return-Redirect": "1"
267
371
  },
268
372
  body: new URLSearchParams({ csrfToken, callbackUrl }),
269
- credentials: __AUTHJS.credentials
373
+ credentials: config.credentials
270
374
  });
271
375
  const data = await res.json();
272
- broadcast().postMessage({ event: "session", data: { trigger: "signout" } });
273
- if (options?.redirect ?? true) {
376
+ if (redirect) {
274
377
  const url = data.url ?? callbackUrl;
275
378
  window.location.href = url;
276
379
  if (url.includes("#")) {
277
380
  window.location.reload();
278
381
  }
279
- return;
382
+ return void 0;
280
383
  }
281
- await __AUTHJS._getSession({ event: "storage" });
384
+ await config.fetchSession?.({ event: "storage" });
282
385
  return data;
283
386
  }
284
- function SessionProvider(props) {
285
- if (!SessionContext) {
286
- throw new Error("React Context is unavailable in Server Components");
287
- }
288
- const { children, refetchInterval, refetchWhenOffline } = props;
289
- const __AUTHJS = authConfigManager.getConfig();
290
- const hasInitialSession = props.session !== void 0;
291
- __AUTHJS._lastSync = hasInitialSession ? now() : 0;
292
- const [session, setSession] = React2.useState(() => {
293
- if (hasInitialSession) {
294
- __AUTHJS._session = props.session;
295
- }
296
- return props.session;
297
- });
298
- const [loading, setLoading] = React2.useState(!hasInitialSession);
299
- React2.useEffect(() => {
300
- __AUTHJS._getSession = async ({ event } = {}) => {
301
- try {
302
- const storageEvent = event === "storage";
303
- if (storageEvent || __AUTHJS._session === void 0) {
304
- __AUTHJS._lastSync = now();
305
- __AUTHJS._session = await getSession({
306
- broadcast: !storageEvent
307
- });
308
- setSession(__AUTHJS._session);
309
- return;
310
- }
311
- if (
312
- // If there is no time defined for when a session should be considered
313
- // stale, then it's okay to use the value we have until an event is
314
- // triggered which updates it
315
- !event || // If the client doesn't have a session then we don't need to call
316
- // the server to check if it does (if they have signed in via another
317
- // tab or window that will come through as a "stroage" event
318
- // event anyway)
319
- __AUTHJS._session === null || // Bail out early if the client session is not stale yet
320
- now() < __AUTHJS._lastSync
321
- ) {
322
- return;
323
- }
324
- __AUTHJS._lastSync = now();
325
- __AUTHJS._session = await getSession();
326
- setSession(__AUTHJS._session);
327
- } catch (error) {
328
- logger.error(new ClientSessionError(error.message, error));
329
- } finally {
330
- setLoading(false);
331
- }
332
- };
333
- __AUTHJS._getSession();
334
- return () => {
335
- __AUTHJS._lastSync = 0;
336
- __AUTHJS._session = void 0;
337
- __AUTHJS._getSession = () => {
338
- };
339
- };
340
- }, []);
341
- React2.useEffect(() => {
342
- const handle = () => __AUTHJS._getSession({ event: "storage" });
343
- broadcast().addEventListener("message", handle);
344
- return () => broadcast().removeEventListener("message", handle);
345
- }, []);
346
- React2.useEffect(() => {
347
- const { refetchOnWindowFocus = true } = props;
348
- const visibilityHandler = () => {
349
- if (refetchOnWindowFocus && document.visibilityState === "visible") {
350
- __AUTHJS._getSession({ event: "visibilitychange" });
351
- }
352
- };
353
- document.addEventListener("visibilitychange", visibilityHandler, false);
354
- return () => document.removeEventListener("visibilitychange", visibilityHandler, false);
355
- }, [props.refetchOnWindowFocus]);
356
- const isOnline = useOnline();
357
- const shouldRefetch = refetchWhenOffline !== false || isOnline;
358
- React2.useEffect(() => {
359
- if (refetchInterval && shouldRefetch) {
360
- const refetchIntervalTimer = setInterval(() => {
361
- if (__AUTHJS._session) {
362
- __AUTHJS._getSession({ event: "poll" });
363
- }
364
- }, refetchInterval * 1e3);
365
- return () => clearInterval(refetchIntervalTimer);
366
- }
367
- }, [refetchInterval, shouldRefetch]);
368
- const value = React2.useMemo(
369
- () => ({
370
- data: session,
371
- status: loading ? "loading" : session ? "authenticated" : "unauthenticated",
372
- async update(data) {
373
- if (loading || !session) {
374
- return;
375
- }
376
- setLoading(true);
377
- const newSession = await fetchData(
378
- "session",
379
- __AUTHJS,
380
- logger,
381
- typeof data === "undefined" ? void 0 : { body: { csrfToken: await getCsrfToken(), data } }
382
- );
383
- setLoading(false);
384
- if (newSession) {
385
- setSession(newSession);
386
- broadcast().postMessage({
387
- event: "session",
388
- data: { trigger: "getSession" }
389
- });
390
- }
391
- return newSession;
392
- }
393
- }),
394
- [session, loading]
395
- );
396
- return /* @__PURE__ */ React2.createElement(SessionContext.Provider, { value }, children);
397
- }
398
387
  // Annotate the CommonJS export names for ESM import in node:
399
388
  0 && (module.exports = {
400
389
  SessionContext,