@onjunctiondev/auth 0.1.3 → 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/dist/index.cjs +87 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -7
- package/dist/react/index.cjs +65 -3
- package/dist/react/index.d.cts +31 -1
- package/dist/react/index.d.ts +31 -1
- package/dist/react/index.js +62 -3
- package/package.json +9 -1
package/dist/index.cjs
CHANGED
|
@@ -61,29 +61,80 @@ function localStorageStorage(key) {
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
function cookieStorage(key) {
|
|
65
|
+
const canUse = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
66
|
+
return {
|
|
67
|
+
get() {
|
|
68
|
+
if (!canUse) return null;
|
|
69
|
+
try {
|
|
70
|
+
const jar = globalThis.document.cookie;
|
|
71
|
+
if (!jar) return null;
|
|
72
|
+
const prefix = `${key}=`;
|
|
73
|
+
for (const part of jar.split("; ")) {
|
|
74
|
+
if (part.startsWith(prefix)) {
|
|
75
|
+
const value = part.slice(prefix.length);
|
|
76
|
+
if (!value) return null;
|
|
77
|
+
return JSON.parse(decodeURIComponent(value));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
set(session) {
|
|
86
|
+
if (!canUse) return;
|
|
87
|
+
try {
|
|
88
|
+
const doc = globalThis.document;
|
|
89
|
+
const loc = globalThis.location;
|
|
90
|
+
const isHttps = loc?.protocol === "https:";
|
|
91
|
+
const secure = isHttps ? "; Secure" : "";
|
|
92
|
+
if (session) {
|
|
93
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
94
|
+
const maxAge = Math.max(0, session.expires_at - now);
|
|
95
|
+
const value = encodeURIComponent(JSON.stringify(session));
|
|
96
|
+
doc.cookie = `${key}=${value}; Path=/; Max-Age=${maxAge}; SameSite=Lax${secure}`;
|
|
97
|
+
} else {
|
|
98
|
+
doc.cookie = `${key}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
64
105
|
|
|
65
106
|
// src/client.ts
|
|
66
|
-
var DEFAULT_STORAGE_KEY = "
|
|
67
|
-
function pickStorage(opts) {
|
|
68
|
-
const key = opts.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
107
|
+
var DEFAULT_STORAGE_KEY = "__junction_session";
|
|
108
|
+
function pickStorage(opts, key) {
|
|
69
109
|
if (opts.storage === "memory") return memoryStorage();
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
110
|
+
if (opts.storage === "localStorage") return localStorageStorage(key);
|
|
111
|
+
const hasDocument = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
112
|
+
if (hasDocument) return cookieStorage(key);
|
|
73
113
|
return memoryStorage();
|
|
74
114
|
}
|
|
75
115
|
var AuthClient = class {
|
|
76
116
|
#deps;
|
|
77
117
|
#listeners = /* @__PURE__ */ new Set();
|
|
118
|
+
#channel;
|
|
119
|
+
#selfId;
|
|
78
120
|
// Refresh in-flight — concurrent 401s share one refresh so we don't
|
|
79
121
|
// race-rotate the single-use token on the server.
|
|
80
122
|
#inflightRefresh = null;
|
|
81
123
|
constructor(opts) {
|
|
124
|
+
const key = opts.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
82
125
|
this.#deps = {
|
|
83
126
|
url: opts.url.replace(/\/+$/, ""),
|
|
84
127
|
fetchFn: opts.fetch ?? ((...args) => fetch(...args)),
|
|
85
|
-
storage: pickStorage(opts)
|
|
128
|
+
storage: pickStorage(opts, key)
|
|
86
129
|
};
|
|
130
|
+
this.#selfId = randomId();
|
|
131
|
+
this.#channel = openChannel(`junction.auth.${key}`);
|
|
132
|
+
this.#channel?.addEventListener("message", (e) => {
|
|
133
|
+
const payload = e.data;
|
|
134
|
+
if (!payload || payload.from === this.#selfId) return;
|
|
135
|
+
this.#deps.storage.set(payload.session);
|
|
136
|
+
this.#emitLocal(payload.event, payload.session);
|
|
137
|
+
});
|
|
87
138
|
}
|
|
88
139
|
// --- public API --------------------------------------------------------
|
|
89
140
|
async signUp(credentials) {
|
|
@@ -202,7 +253,22 @@ var AuthClient = class {
|
|
|
202
253
|
async #rawFetch(path, init) {
|
|
203
254
|
return this.#deps.fetchFn(`${this.#deps.url}${path}`, init);
|
|
204
255
|
}
|
|
256
|
+
// Emits locally AND broadcasts to peer tabs.
|
|
205
257
|
#emit(event, session) {
|
|
258
|
+
this.#emitLocal(event, session);
|
|
259
|
+
try {
|
|
260
|
+
this.#channel?.postMessage({
|
|
261
|
+
from: this.#selfId,
|
|
262
|
+
event,
|
|
263
|
+
session
|
|
264
|
+
});
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Emits to in-process subscribers only — used both by local actions
|
|
269
|
+
// (via #emit, which also broadcasts) and by inbound broadcast messages
|
|
270
|
+
// (direct, so we don't re-broadcast and loop).
|
|
271
|
+
#emitLocal(event, session) {
|
|
206
272
|
for (const l of this.#listeners) {
|
|
207
273
|
try {
|
|
208
274
|
l(event, session);
|
|
@@ -211,6 +277,20 @@ var AuthClient = class {
|
|
|
211
277
|
}
|
|
212
278
|
}
|
|
213
279
|
};
|
|
280
|
+
function openChannel(name) {
|
|
281
|
+
const Ctor = globalThis.BroadcastChannel;
|
|
282
|
+
if (!Ctor) return null;
|
|
283
|
+
try {
|
|
284
|
+
return new Ctor(name);
|
|
285
|
+
} catch {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function randomId() {
|
|
290
|
+
const crypto = globalThis.crypto;
|
|
291
|
+
if (crypto?.randomUUID) return crypto.randomUUID();
|
|
292
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
293
|
+
}
|
|
214
294
|
function errorFrom(code, message, status) {
|
|
215
295
|
return { code, message, status };
|
|
216
296
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -34,7 +34,7 @@ type AuthEvent = "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "USER_UPDATED"
|
|
|
34
34
|
type AuthStateChangeHandler = (event: AuthEvent, session: AuthSession | null) => void;
|
|
35
35
|
type ClientOptions = {
|
|
36
36
|
url: string;
|
|
37
|
-
storage?: "localStorage" | "memory";
|
|
37
|
+
storage?: "cookie" | "localStorage" | "memory";
|
|
38
38
|
storageKey?: string;
|
|
39
39
|
fetch?: typeof fetch;
|
|
40
40
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ type AuthEvent = "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "USER_UPDATED"
|
|
|
34
34
|
type AuthStateChangeHandler = (event: AuthEvent, session: AuthSession | null) => void;
|
|
35
35
|
type ClientOptions = {
|
|
36
36
|
url: string;
|
|
37
|
-
storage?: "localStorage" | "memory";
|
|
37
|
+
storage?: "cookie" | "localStorage" | "memory";
|
|
38
38
|
storageKey?: string;
|
|
39
39
|
fetch?: typeof fetch;
|
|
40
40
|
};
|
package/dist/index.js
CHANGED
|
@@ -34,29 +34,80 @@ function localStorageStorage(key) {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
+
function cookieStorage(key) {
|
|
38
|
+
const canUse = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
39
|
+
return {
|
|
40
|
+
get() {
|
|
41
|
+
if (!canUse) return null;
|
|
42
|
+
try {
|
|
43
|
+
const jar = globalThis.document.cookie;
|
|
44
|
+
if (!jar) return null;
|
|
45
|
+
const prefix = `${key}=`;
|
|
46
|
+
for (const part of jar.split("; ")) {
|
|
47
|
+
if (part.startsWith(prefix)) {
|
|
48
|
+
const value = part.slice(prefix.length);
|
|
49
|
+
if (!value) return null;
|
|
50
|
+
return JSON.parse(decodeURIComponent(value));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
set(session) {
|
|
59
|
+
if (!canUse) return;
|
|
60
|
+
try {
|
|
61
|
+
const doc = globalThis.document;
|
|
62
|
+
const loc = globalThis.location;
|
|
63
|
+
const isHttps = loc?.protocol === "https:";
|
|
64
|
+
const secure = isHttps ? "; Secure" : "";
|
|
65
|
+
if (session) {
|
|
66
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
67
|
+
const maxAge = Math.max(0, session.expires_at - now);
|
|
68
|
+
const value = encodeURIComponent(JSON.stringify(session));
|
|
69
|
+
doc.cookie = `${key}=${value}; Path=/; Max-Age=${maxAge}; SameSite=Lax${secure}`;
|
|
70
|
+
} else {
|
|
71
|
+
doc.cookie = `${key}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
37
78
|
|
|
38
79
|
// src/client.ts
|
|
39
|
-
var DEFAULT_STORAGE_KEY = "
|
|
40
|
-
function pickStorage(opts) {
|
|
41
|
-
const key = opts.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
80
|
+
var DEFAULT_STORAGE_KEY = "__junction_session";
|
|
81
|
+
function pickStorage(opts, key) {
|
|
42
82
|
if (opts.storage === "memory") return memoryStorage();
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
83
|
+
if (opts.storage === "localStorage") return localStorageStorage(key);
|
|
84
|
+
const hasDocument = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
85
|
+
if (hasDocument) return cookieStorage(key);
|
|
46
86
|
return memoryStorage();
|
|
47
87
|
}
|
|
48
88
|
var AuthClient = class {
|
|
49
89
|
#deps;
|
|
50
90
|
#listeners = /* @__PURE__ */ new Set();
|
|
91
|
+
#channel;
|
|
92
|
+
#selfId;
|
|
51
93
|
// Refresh in-flight — concurrent 401s share one refresh so we don't
|
|
52
94
|
// race-rotate the single-use token on the server.
|
|
53
95
|
#inflightRefresh = null;
|
|
54
96
|
constructor(opts) {
|
|
97
|
+
const key = opts.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
55
98
|
this.#deps = {
|
|
56
99
|
url: opts.url.replace(/\/+$/, ""),
|
|
57
100
|
fetchFn: opts.fetch ?? ((...args) => fetch(...args)),
|
|
58
|
-
storage: pickStorage(opts)
|
|
101
|
+
storage: pickStorage(opts, key)
|
|
59
102
|
};
|
|
103
|
+
this.#selfId = randomId();
|
|
104
|
+
this.#channel = openChannel(`junction.auth.${key}`);
|
|
105
|
+
this.#channel?.addEventListener("message", (e) => {
|
|
106
|
+
const payload = e.data;
|
|
107
|
+
if (!payload || payload.from === this.#selfId) return;
|
|
108
|
+
this.#deps.storage.set(payload.session);
|
|
109
|
+
this.#emitLocal(payload.event, payload.session);
|
|
110
|
+
});
|
|
60
111
|
}
|
|
61
112
|
// --- public API --------------------------------------------------------
|
|
62
113
|
async signUp(credentials) {
|
|
@@ -175,7 +226,22 @@ var AuthClient = class {
|
|
|
175
226
|
async #rawFetch(path, init) {
|
|
176
227
|
return this.#deps.fetchFn(`${this.#deps.url}${path}`, init);
|
|
177
228
|
}
|
|
229
|
+
// Emits locally AND broadcasts to peer tabs.
|
|
178
230
|
#emit(event, session) {
|
|
231
|
+
this.#emitLocal(event, session);
|
|
232
|
+
try {
|
|
233
|
+
this.#channel?.postMessage({
|
|
234
|
+
from: this.#selfId,
|
|
235
|
+
event,
|
|
236
|
+
session
|
|
237
|
+
});
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Emits to in-process subscribers only — used both by local actions
|
|
242
|
+
// (via #emit, which also broadcasts) and by inbound broadcast messages
|
|
243
|
+
// (direct, so we don't re-broadcast and loop).
|
|
244
|
+
#emitLocal(event, session) {
|
|
179
245
|
for (const l of this.#listeners) {
|
|
180
246
|
try {
|
|
181
247
|
l(event, session);
|
|
@@ -184,6 +250,20 @@ var AuthClient = class {
|
|
|
184
250
|
}
|
|
185
251
|
}
|
|
186
252
|
};
|
|
253
|
+
function openChannel(name) {
|
|
254
|
+
const Ctor = globalThis.BroadcastChannel;
|
|
255
|
+
if (!Ctor) return null;
|
|
256
|
+
try {
|
|
257
|
+
return new Ctor(name);
|
|
258
|
+
} catch {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function randomId() {
|
|
263
|
+
const crypto = globalThis.crypto;
|
|
264
|
+
if (crypto?.randomUUID) return crypto.randomUUID();
|
|
265
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
266
|
+
}
|
|
187
267
|
function errorFrom(code, message, status) {
|
|
188
268
|
return { code, message, status };
|
|
189
269
|
}
|
package/dist/react/index.cjs
CHANGED
|
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
6
10
|
var __copyProps = (to, from, except, desc) => {
|
|
7
11
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
12
|
for (let key of __getOwnPropNames(from))
|
|
@@ -15,7 +19,65 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
15
19
|
|
|
16
20
|
// src/react/index.ts
|
|
17
21
|
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
JunctionProvider: () => JunctionProvider,
|
|
24
|
+
useJunctionAuth: () => useJunctionAuth,
|
|
25
|
+
useJunctionSession: () => useJunctionSession,
|
|
26
|
+
useJunctionUser: () => useJunctionUser
|
|
27
|
+
});
|
|
18
28
|
module.exports = __toCommonJS(react_exports);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
var JunctionAuthContext = (0, import_react.createContext)(null);
|
|
31
|
+
function JunctionProvider(props) {
|
|
32
|
+
const { client, children } = props;
|
|
33
|
+
const [state, setState] = (0, import_react.useState)(() => ({
|
|
34
|
+
session: client.auth.getSession().data?.session ?? null,
|
|
35
|
+
isLoading: true
|
|
36
|
+
}));
|
|
37
|
+
(0, import_react.useEffect)(() => {
|
|
38
|
+
const sub = client.auth.onAuthStateChange((_event, session) => {
|
|
39
|
+
setState({ session, isLoading: false });
|
|
40
|
+
});
|
|
41
|
+
return () => sub.unsubscribe();
|
|
42
|
+
}, [client]);
|
|
43
|
+
const value = (0, import_react.useMemo)(() => ({ client, state }), [client, state]);
|
|
44
|
+
return (0, import_react.createElement)(JunctionAuthContext.Provider, { value }, children);
|
|
45
|
+
}
|
|
46
|
+
function useCtx() {
|
|
47
|
+
const ctx = (0, import_react.useContext)(JunctionAuthContext);
|
|
48
|
+
if (!ctx) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"Junction auth hook called outside <JunctionProvider>. Wrap your app in <JunctionProvider client={...}>."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return ctx;
|
|
54
|
+
}
|
|
55
|
+
function useJunctionSession() {
|
|
56
|
+
const { state } = useCtx();
|
|
57
|
+
return { session: state.session, isLoading: state.isLoading };
|
|
58
|
+
}
|
|
59
|
+
function useJunctionUser() {
|
|
60
|
+
const { state } = useCtx();
|
|
61
|
+
return { user: state.session?.user ?? null, isLoading: state.isLoading };
|
|
62
|
+
}
|
|
63
|
+
function useJunctionAuth() {
|
|
64
|
+
const { client } = useCtx();
|
|
65
|
+
const signIn = (0, import_react.useCallback)(
|
|
66
|
+
(email, password) => client.auth.signIn({ email, password }),
|
|
67
|
+
[client]
|
|
68
|
+
);
|
|
69
|
+
const signUp = (0, import_react.useCallback)(
|
|
70
|
+
(email, password) => client.auth.signUp({ email, password }),
|
|
71
|
+
[client]
|
|
72
|
+
);
|
|
73
|
+
const signOut = (0, import_react.useCallback)(() => client.auth.signOut(), [client]);
|
|
74
|
+
const refreshSession = (0, import_react.useCallback)(() => client.auth.refreshSession(), [client]);
|
|
75
|
+
return { signIn, signUp, signOut, refreshSession };
|
|
76
|
+
}
|
|
77
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
78
|
+
0 && (module.exports = {
|
|
79
|
+
JunctionProvider,
|
|
80
|
+
useJunctionAuth,
|
|
81
|
+
useJunctionSession,
|
|
82
|
+
useJunctionUser
|
|
83
|
+
});
|
package/dist/react/index.d.cts
CHANGED
|
@@ -1,2 +1,32 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { JunctionClient, AuthSession, Result, AuthUser } from '../index.cjs';
|
|
1
4
|
|
|
2
|
-
|
|
5
|
+
type SessionState = {
|
|
6
|
+
session: AuthSession | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
};
|
|
9
|
+
type ContextValue = {
|
|
10
|
+
client: JunctionClient;
|
|
11
|
+
state: SessionState;
|
|
12
|
+
};
|
|
13
|
+
declare function JunctionProvider(props: {
|
|
14
|
+
client: JunctionClient;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): react.FunctionComponentElement<react.ProviderProps<ContextValue | null>>;
|
|
17
|
+
declare function useJunctionSession(): {
|
|
18
|
+
session: AuthSession | null;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
};
|
|
21
|
+
declare function useJunctionUser(): {
|
|
22
|
+
user: AuthUser | null;
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
};
|
|
25
|
+
declare function useJunctionAuth(): {
|
|
26
|
+
signIn: (email: string, password: string) => Promise<Result<AuthSession>>;
|
|
27
|
+
signUp: (email: string, password: string) => Promise<Result<AuthSession>>;
|
|
28
|
+
signOut: () => Promise<Result<null>>;
|
|
29
|
+
refreshSession: () => Promise<Result<AuthSession>>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { JunctionProvider, useJunctionAuth, useJunctionSession, useJunctionUser };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,2 +1,32 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { JunctionClient, AuthSession, Result, AuthUser } from '../index.js';
|
|
1
4
|
|
|
2
|
-
|
|
5
|
+
type SessionState = {
|
|
6
|
+
session: AuthSession | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
};
|
|
9
|
+
type ContextValue = {
|
|
10
|
+
client: JunctionClient;
|
|
11
|
+
state: SessionState;
|
|
12
|
+
};
|
|
13
|
+
declare function JunctionProvider(props: {
|
|
14
|
+
client: JunctionClient;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): react.FunctionComponentElement<react.ProviderProps<ContextValue | null>>;
|
|
17
|
+
declare function useJunctionSession(): {
|
|
18
|
+
session: AuthSession | null;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
};
|
|
21
|
+
declare function useJunctionUser(): {
|
|
22
|
+
user: AuthUser | null;
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
};
|
|
25
|
+
declare function useJunctionAuth(): {
|
|
26
|
+
signIn: (email: string, password: string) => Promise<Result<AuthSession>>;
|
|
27
|
+
signUp: (email: string, password: string) => Promise<Result<AuthSession>>;
|
|
28
|
+
signOut: () => Promise<Result<null>>;
|
|
29
|
+
refreshSession: () => Promise<Result<AuthSession>>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { JunctionProvider, useJunctionAuth, useJunctionSession, useJunctionUser };
|
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,63 @@
|
|
|
1
1
|
// src/react/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
createElement,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState
|
|
10
|
+
} from "react";
|
|
11
|
+
var JunctionAuthContext = createContext(null);
|
|
12
|
+
function JunctionProvider(props) {
|
|
13
|
+
const { client, children } = props;
|
|
14
|
+
const [state, setState] = useState(() => ({
|
|
15
|
+
session: client.auth.getSession().data?.session ?? null,
|
|
16
|
+
isLoading: true
|
|
17
|
+
}));
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const sub = client.auth.onAuthStateChange((_event, session) => {
|
|
20
|
+
setState({ session, isLoading: false });
|
|
21
|
+
});
|
|
22
|
+
return () => sub.unsubscribe();
|
|
23
|
+
}, [client]);
|
|
24
|
+
const value = useMemo(() => ({ client, state }), [client, state]);
|
|
25
|
+
return createElement(JunctionAuthContext.Provider, { value }, children);
|
|
26
|
+
}
|
|
27
|
+
function useCtx() {
|
|
28
|
+
const ctx = useContext(JunctionAuthContext);
|
|
29
|
+
if (!ctx) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Junction auth hook called outside <JunctionProvider>. Wrap your app in <JunctionProvider client={...}>."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return ctx;
|
|
35
|
+
}
|
|
36
|
+
function useJunctionSession() {
|
|
37
|
+
const { state } = useCtx();
|
|
38
|
+
return { session: state.session, isLoading: state.isLoading };
|
|
39
|
+
}
|
|
40
|
+
function useJunctionUser() {
|
|
41
|
+
const { state } = useCtx();
|
|
42
|
+
return { user: state.session?.user ?? null, isLoading: state.isLoading };
|
|
43
|
+
}
|
|
44
|
+
function useJunctionAuth() {
|
|
45
|
+
const { client } = useCtx();
|
|
46
|
+
const signIn = useCallback(
|
|
47
|
+
(email, password) => client.auth.signIn({ email, password }),
|
|
48
|
+
[client]
|
|
49
|
+
);
|
|
50
|
+
const signUp = useCallback(
|
|
51
|
+
(email, password) => client.auth.signUp({ email, password }),
|
|
52
|
+
[client]
|
|
53
|
+
);
|
|
54
|
+
const signOut = useCallback(() => client.auth.signOut(), [client]);
|
|
55
|
+
const refreshSession = useCallback(() => client.auth.refreshSession(), [client]);
|
|
56
|
+
return { signIn, signUp, signOut, refreshSession };
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
JunctionProvider,
|
|
60
|
+
useJunctionAuth,
|
|
61
|
+
useJunctionSession,
|
|
62
|
+
useJunctionUser
|
|
63
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onjunctiondev/auth",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Headless auth SDK for Junction — email/password sessions with per-project JWTs and RLS claims.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://onjunction.dev",
|
|
@@ -40,9 +40,17 @@
|
|
|
40
40
|
"check-types": "tsc --noEmit",
|
|
41
41
|
"prepublishOnly": "pnpm build"
|
|
42
42
|
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"react": { "optional": true }
|
|
48
|
+
},
|
|
43
49
|
"devDependencies": {
|
|
44
50
|
"@junction/typescript-config": "workspace:*",
|
|
45
51
|
"@types/node": "26.4.1",
|
|
52
|
+
"@types/react": "^19.0.0",
|
|
53
|
+
"react": "^19.0.0",
|
|
46
54
|
"tsup": "^8.3.5",
|
|
47
55
|
"typescript": "~5.7.0"
|
|
48
56
|
}
|