@relipay/react 0.1.0-beta.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/AGENTS.md ADDED
@@ -0,0 +1,45 @@
1
+ # AGENTS.md — `@relipay/react`
2
+
3
+ React hooks + headless components for end-user auth state.
4
+
5
+ ## When to use
6
+
7
+ The user wants browser-side knowledge of "is this user signed in" — to render `<SignedIn>` / `<SignedOut>` regions, show the user's email in a header, gate UI features.
8
+
9
+ **Do NOT use** for:
10
+ - Server-only auth (use `@relipay/nextjs/server` `auth()`).
11
+ - Sign-in form rendering — `@relipay/react` is headless. Build your own form, post to your `/api/sign-in` route, and call `useRelipay().refresh()` to update the provider after.
12
+
13
+ ## Surface
14
+
15
+ ```ts
16
+ <RelipayProvider apiUrl initialUser? accessToken? meEndpoint?>{children}</RelipayProvider>
17
+ useUser() → { user, signedIn, loading }
18
+ useRelipay() → { refresh }
19
+ <SignedIn>{children}</SignedIn> → renders only if signed in
20
+ <SignedOut>{children}</SignedOut>
21
+ <Loading>{children}</Loading>
22
+ ```
23
+
24
+ ## Auth model — important to internalise
25
+
26
+ The browser **never sees the Application secret key**. The flow is:
27
+
28
+ ```
29
+ Browser → your customer's server → @relipay/node → ReliPay
30
+
31
+ returns { user, accessToken } via cookie
32
+
33
+ SSR seeds <RelipayProvider initialUser={user} accessToken={accessToken}>
34
+ ```
35
+
36
+ The `accessToken` here is the **end-user's** JWT, *not* the Application secret. It's safe to expose to client JS for the duration of the user's session — but rotate it via cookie on the server.
37
+
38
+ The provider's `meEndpoint` defaults to `/api/v1/users/me/by-token`. The server-side ReliPay API today requires both an Application secret and a user JWT for `/users/me` — so for the browser to call it directly, your customer's app needs a tiny passthrough route that adds the secret. Or pre-fetch `initialUser` server-side (the recommended pattern) and skip the round-trip.
39
+
40
+ ## What NOT to do
41
+
42
+ - Don't ship the Application secret key to the browser. Ever. Even in a hidden field, even encoded.
43
+ - Don't use `useUser()` outside `<RelipayProvider>` — it throws explicitly.
44
+ - Don't add styled sign-in/sign-up components here. Headless on purpose. Customers style their own.
45
+ - Don't pre-render `<SignedIn>` content during SSR with `loading=true` — the helper returns null until the provider resolves, which is correct.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Browser-side ReliPay client.
3
+ *
4
+ * Differs from `@relipay/node` in two important ways:
5
+ * 1. **Auth model**. The browser never sees the server-side secret key.
6
+ * Instead, your customer's *server* signs in via @relipay/node and
7
+ * hands the browser a short-lived `accessToken` (the user JWT). All
8
+ * browser-side calls use that token directly — there's no separate
9
+ * "Application API key" leg from the browser.
10
+ * 2. **No secrets in the bundle**. The only ReliPay credential the
11
+ * browser holds is the user's JWT — and even that is rotatable.
12
+ *
13
+ * `apiUrl` is required. Bring-your-own fetch is supported for SSR/SSE
14
+ * shims and tests.
15
+ */
16
+ import type { EndUserDto, RelipayError as RelipayErrorShape } from '@relipay/shared-types';
17
+ export interface ReliPayBrowserConfig {
18
+ apiUrl: string;
19
+ fetch?: typeof fetch;
20
+ }
21
+ export declare class RelipayError extends Error implements RelipayErrorShape {
22
+ readonly code: string;
23
+ readonly fix: string | undefined;
24
+ readonly docs: string | undefined;
25
+ readonly statusCode: number | undefined;
26
+ constructor(error: RelipayErrorShape & {
27
+ statusCode?: number;
28
+ });
29
+ }
30
+ export declare class RelipayBrowserClient {
31
+ private readonly apiUrl;
32
+ private readonly fetchImpl;
33
+ constructor(config: ReliPayBrowserConfig);
34
+ /**
35
+ * Fetch the current end-user given an access token. Returns null on
36
+ * USER_TOKEN_INVALID so callers can render signed-out state without
37
+ * try/catch noise.
38
+ *
39
+ * NOTE: this hits a *user-token-only* endpoint that doesn't exist yet on
40
+ * the server. The browser SDK uses a thin proxy at
41
+ * `/api/v1/users/me-by-token` that the customer's server will provide
42
+ * (because the API surface today still requires the secret key for
43
+ * /users/me). For Phase 5 we expose the contract — the server-side
44
+ * proxy is one route handler in the customer's Next.js app.
45
+ *
46
+ * For deployments that *do* expose user-token-only endpoints, override
47
+ * `meEndpoint` in config.
48
+ */
49
+ getCurrentUser(accessToken: string, meEndpoint?: string): Promise<EndUserDto | null>;
50
+ private raw;
51
+ }
52
+ export type { EndUserDto } from '@relipay/shared-types';
53
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,qBAAa,YAAa,SAAQ,KAAM,YAAW,iBAAiB;IAClE,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,SAAgB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,SAAgB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;gBACnC,KAAK,EAAE,iBAAiB,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAQ/D;AAOD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,MAAM,EAAE,oBAAoB;IAYxC;;;;;;;;;;;;;;OAcG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,SAA8B,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAYjG,GAAG;CA2BlB;AAED,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Browser-side ReliPay client.
3
+ *
4
+ * Differs from `@relipay/node` in two important ways:
5
+ * 1. **Auth model**. The browser never sees the server-side secret key.
6
+ * Instead, your customer's *server* signs in via @relipay/node and
7
+ * hands the browser a short-lived `accessToken` (the user JWT). All
8
+ * browser-side calls use that token directly — there's no separate
9
+ * "Application API key" leg from the browser.
10
+ * 2. **No secrets in the bundle**. The only ReliPay credential the
11
+ * browser holds is the user's JWT — and even that is rotatable.
12
+ *
13
+ * `apiUrl` is required. Bring-your-own fetch is supported for SSR/SSE
14
+ * shims and tests.
15
+ */
16
+ export class RelipayError extends Error {
17
+ code;
18
+ fix;
19
+ docs;
20
+ statusCode;
21
+ constructor(error) {
22
+ super(error.message);
23
+ this.name = 'RelipayError';
24
+ this.code = error.code;
25
+ this.fix = error.fix;
26
+ this.docs = error.docs;
27
+ this.statusCode = error.statusCode;
28
+ }
29
+ }
30
+ export class RelipayBrowserClient {
31
+ apiUrl;
32
+ fetchImpl;
33
+ constructor(config) {
34
+ if (!config.apiUrl) {
35
+ throw new RelipayError({
36
+ code: 'CONFIG_MISSING_API_URL',
37
+ message: '@relipay/react: apiUrl is required.',
38
+ fix: 'Pass apiUrl when constructing RelipayBrowserClient or via <RelipayProvider apiUrl=...>',
39
+ });
40
+ }
41
+ this.apiUrl = config.apiUrl.replace(/\/$/, '');
42
+ this.fetchImpl = config.fetch ?? fetch.bind(globalThis);
43
+ }
44
+ /**
45
+ * Fetch the current end-user given an access token. Returns null on
46
+ * USER_TOKEN_INVALID so callers can render signed-out state without
47
+ * try/catch noise.
48
+ *
49
+ * NOTE: this hits a *user-token-only* endpoint that doesn't exist yet on
50
+ * the server. The browser SDK uses a thin proxy at
51
+ * `/api/v1/users/me-by-token` that the customer's server will provide
52
+ * (because the API surface today still requires the secret key for
53
+ * /users/me). For Phase 5 we expose the contract — the server-side
54
+ * proxy is one route handler in the customer's Next.js app.
55
+ *
56
+ * For deployments that *do* expose user-token-only endpoints, override
57
+ * `meEndpoint` in config.
58
+ */
59
+ async getCurrentUser(accessToken, meEndpoint = '/api/v1/users/me/by-token') {
60
+ try {
61
+ const res = await this.raw('GET', meEndpoint, undefined, accessToken);
62
+ return res.data;
63
+ }
64
+ catch (err) {
65
+ if (err instanceof RelipayError && err.code === 'USER_TOKEN_INVALID') {
66
+ return null;
67
+ }
68
+ throw err;
69
+ }
70
+ }
71
+ async raw(method, path, body, accessToken) {
72
+ const res = await this.fetchImpl(`${this.apiUrl}${path}`, {
73
+ method,
74
+ headers: {
75
+ ...(accessToken ? { 'X-Relipay-User-Token': accessToken } : {}),
76
+ ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
77
+ },
78
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
79
+ credentials: 'include',
80
+ });
81
+ const json = (await res.json().catch(() => ({})));
82
+ if (!res.ok || ('success' in json && json.success === false)) {
83
+ const err = 'error' in json
84
+ ? json.error
85
+ : { code: 'UNKNOWN_ERROR', message: `HTTP ${res.status}` };
86
+ throw new RelipayError({ ...err, statusCode: res.status });
87
+ }
88
+ return { data: json.data, status: res.status };
89
+ }
90
+ }
91
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Headless wrappers — render-prop style components.
3
+ *
4
+ * `<SignedIn>` renders children iff there's a user.
5
+ * `<SignedOut>` renders children iff signed out.
6
+ * `<Loading>` renders children while resolving.
7
+ *
8
+ * These are intentionally header-less — no opinions about your auth UI.
9
+ * Your customer's app supplies the styled forms; we just give them the
10
+ * "is the user signed in?" primitive.
11
+ */
12
+ import * as React from 'react';
13
+ export declare function SignedIn({ children }: {
14
+ children: React.ReactNode;
15
+ }): React.JSX.Element | null;
16
+ export declare function SignedOut({ children }: {
17
+ children: React.ReactNode;
18
+ }): React.JSX.Element | null;
19
+ export declare function Loading({ children }: {
20
+ children: React.ReactNode;
21
+ }): React.JSX.Element | null;
22
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAI9F;AAED,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAI/F;AAED,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAG7F"}
@@ -0,0 +1,19 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useUser } from './hooks.js';
3
+ export function SignedIn({ children }) {
4
+ const { signedIn, loading } = useUser();
5
+ if (loading)
6
+ return null;
7
+ return signedIn ? _jsx(_Fragment, { children: children }) : null;
8
+ }
9
+ export function SignedOut({ children }) {
10
+ const { signedIn, loading } = useUser();
11
+ if (loading)
12
+ return null;
13
+ return signedIn ? null : _jsx(_Fragment, { children: children });
14
+ }
15
+ export function Loading({ children }) {
16
+ const { loading } = useUser();
17
+ return loading ? _jsx(_Fragment, { children: children }) : null;
18
+ }
19
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":";AAaA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAiC;IAClE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACxC,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,QAAQ,CAAC,CAAC,CAAC,4BAAG,QAAQ,GAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAE,QAAQ,EAAiC;IACnE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACxC,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,4BAAG,QAAQ,GAAI,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,EAAE,QAAQ,EAAiC;IACjE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,4BAAG,QAAQ,GAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * RelipayProvider — wraps the app, exposes user + signed-in state.
3
+ *
4
+ * Two ways to feed it:
5
+ * 1. Initial user + access token from your SSR (Next.js App Router server
6
+ * component): `<RelipayProvider initialUser={...} initialAccessToken={...}>`
7
+ * 2. Fetch on mount: pass only `accessToken` — the provider calls
8
+ * getCurrentUser on mount + on token change.
9
+ *
10
+ * Auth state changes (sign-in, sign-out, refresh) are usually handled by
11
+ * the customer's server (cookie rotation). Components here read from the
12
+ * context, so they reflect whatever the server says about the user on the
13
+ * next page load.
14
+ */
15
+ import * as React from 'react';
16
+ import type { EndUserDto } from '@relipay/shared-types';
17
+ import { RelipayBrowserClient } from './client.js';
18
+ export interface RelipayContextValue {
19
+ user: EndUserDto | null;
20
+ signedIn: boolean;
21
+ loading: boolean;
22
+ client: RelipayBrowserClient;
23
+ /** Manual refresh — re-fetch the current user. Useful after sign-in. */
24
+ refresh: () => Promise<void>;
25
+ }
26
+ export interface RelipayProviderProps {
27
+ children: React.ReactNode;
28
+ apiUrl: string;
29
+ /** Pre-fetched user to seed initial render (typical SSR pattern). */
30
+ initialUser?: EndUserDto | null;
31
+ /** Token to use for client-side calls. Provider re-fetches user when this changes. */
32
+ accessToken?: string | null;
33
+ /** Override the /me-by-token endpoint your customer server proxies to ReliPay. */
34
+ meEndpoint?: string;
35
+ }
36
+ export declare function RelipayProvider({ children, apiUrl, initialUser, accessToken, meEndpoint, }: RelipayProviderProps): React.JSX.Element;
37
+ /** Internal — use the public hooks instead. */
38
+ export declare function useRelipayContext(): RelipayContextValue;
39
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,wEAAwE;IACxE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,WAAW,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,MAAM,EACN,WAAkB,EAClB,WAAkB,EAClB,UAAU,GACX,EAAE,oBAAoB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAiC1C;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,IAAI,mBAAmB,CAQvD"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * RelipayProvider — wraps the app, exposes user + signed-in state.
4
+ *
5
+ * Two ways to feed it:
6
+ * 1. Initial user + access token from your SSR (Next.js App Router server
7
+ * component): `<RelipayProvider initialUser={...} initialAccessToken={...}>`
8
+ * 2. Fetch on mount: pass only `accessToken` — the provider calls
9
+ * getCurrentUser on mount + on token change.
10
+ *
11
+ * Auth state changes (sign-in, sign-out, refresh) are usually handled by
12
+ * the customer's server (cookie rotation). Components here read from the
13
+ * context, so they reflect whatever the server says about the user on the
14
+ * next page load.
15
+ */
16
+ import * as React from 'react';
17
+ import { RelipayBrowserClient } from './client.js';
18
+ const Ctx = React.createContext(null);
19
+ export function RelipayProvider({ children, apiUrl, initialUser = null, accessToken = null, meEndpoint, }) {
20
+ const client = React.useMemo(() => new RelipayBrowserClient({ apiUrl }), [apiUrl]);
21
+ const [user, setUser] = React.useState(initialUser);
22
+ const [loading, setLoading] = React.useState(initialUser === null && Boolean(accessToken));
23
+ const refresh = React.useCallback(async () => {
24
+ if (!accessToken) {
25
+ setUser(null);
26
+ setLoading(false);
27
+ return;
28
+ }
29
+ setLoading(true);
30
+ try {
31
+ const u = await client.getCurrentUser(accessToken, meEndpoint);
32
+ setUser(u);
33
+ }
34
+ catch {
35
+ // Network or unknown error → treat as signed-out for UI purposes.
36
+ setUser(null);
37
+ }
38
+ finally {
39
+ setLoading(false);
40
+ }
41
+ }, [client, accessToken, meEndpoint]);
42
+ React.useEffect(() => {
43
+ void refresh();
44
+ }, [refresh]);
45
+ const value = React.useMemo(() => ({ user, signedIn: user !== null, loading, client, refresh }), [user, loading, client, refresh]);
46
+ return _jsx(Ctx.Provider, { value: value, children: children });
47
+ }
48
+ /** Internal — use the public hooks instead. */
49
+ export function useRelipayContext() {
50
+ const ctx = React.useContext(Ctx);
51
+ if (!ctx) {
52
+ throw new Error('@relipay/react: hooks must be used inside <RelipayProvider>. Wrap your app at the root.');
53
+ }
54
+ return ctx;
55
+ }
56
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAWnD,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,CAA6B,IAAI,CAAC,CAAC;AAalE,MAAM,UAAU,eAAe,CAAC,EAC9B,QAAQ,EACR,MAAM,EACN,WAAW,GAAG,IAAI,EAClB,WAAW,GAAG,IAAI,EAClB,UAAU,GACW;IACrB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACnF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAoB,WAAW,CAAC,CAAC;IACvE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAU,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IAEpG,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAmB,EAAE;QAC1D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;YAC/D,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;IAEtC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,KAAK,OAAO,EAAE,CAAC;IACjB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,KAAK,GAAwB,KAAK,CAAC,OAAO,CAC9C,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EACnE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CACjC,CAAC;IAEF,OAAO,KAAC,GAAG,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAgB,CAAC;AAC/D,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Public hooks. Idiomatic React — same shape Clerk and Auth.js use, so
3
+ * developers (and AI agents) recognise them immediately.
4
+ */
5
+ import type { EndUserDto } from '@relipay/shared-types';
6
+ /**
7
+ * The current end-user, or `null` if signed out. The narrowing is the same
8
+ * pattern Clerk uses — check `user` for null, then TypeScript narrows.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { user, signedIn, loading } = useUser();
13
+ * if (loading) return <Spinner />;
14
+ * if (!signedIn) return <SignedOut />;
15
+ * return <p>Hi {user.email}</p>;
16
+ * ```
17
+ */
18
+ export declare function useUser(): {
19
+ user: EndUserDto | null;
20
+ signedIn: boolean;
21
+ loading: boolean;
22
+ };
23
+ /**
24
+ * Manual session refresh. Usually called after a sign-in / sign-out
25
+ * round-trip the customer's server handles, so the provider re-fetches
26
+ * the latest user state.
27
+ */
28
+ export declare function useRelipay(): {
29
+ refresh: () => Promise<void>;
30
+ };
31
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,IAAI;IACzB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB,CAGA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAGA"}
package/dist/hooks.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Public hooks. Idiomatic React — same shape Clerk and Auth.js use, so
3
+ * developers (and AI agents) recognise them immediately.
4
+ */
5
+ import { useRelipayContext } from './context.js';
6
+ /**
7
+ * The current end-user, or `null` if signed out. The narrowing is the same
8
+ * pattern Clerk uses — check `user` for null, then TypeScript narrows.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { user, signedIn, loading } = useUser();
13
+ * if (loading) return <Spinner />;
14
+ * if (!signedIn) return <SignedOut />;
15
+ * return <p>Hi {user.email}</p>;
16
+ * ```
17
+ */
18
+ export function useUser() {
19
+ const ctx = useRelipayContext();
20
+ return { user: ctx.user, signedIn: ctx.signedIn, loading: ctx.loading };
21
+ }
22
+ /**
23
+ * Manual session refresh. Usually called after a sign-in / sign-out
24
+ * round-trip the customer's server handles, so the provider re-fetches
25
+ * the latest user state.
26
+ */
27
+ export function useRelipay() {
28
+ const ctx = useRelipayContext();
29
+ return { refresh: ctx.refresh };
30
+ }
31
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO;IAKrB,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;IAChC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;AAC1E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU;IAGxB,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;IAChC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @relipay/react — React hooks + headless components for end-user auth.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // Server (Next.js App Router root layout)
7
+ * const user = await getCurrentUserFromCookie();
8
+ * return (
9
+ * <RelipayProvider apiUrl={env.RELIPAY_URL} initialUser={user} accessToken={user?.accessToken ?? null}>
10
+ * {children}
11
+ * </RelipayProvider>
12
+ * );
13
+ *
14
+ * // Client component
15
+ * import { useUser, SignedIn, SignedOut } from '@relipay/react';
16
+ *
17
+ * function Header() {
18
+ * const { user } = useUser();
19
+ * return (
20
+ * <>
21
+ * <SignedIn>Hi, {user!.email}</SignedIn>
22
+ * <SignedOut><a href="/sign-in">Sign in</a></SignedOut>
23
+ * </>
24
+ * );
25
+ * }
26
+ * ```
27
+ */
28
+ export { RelipayProvider } from './context.js';
29
+ export type { RelipayContextValue, RelipayProviderProps } from './context.js';
30
+ export { useUser, useRelipay } from './hooks.js';
31
+ export { SignedIn, SignedOut, Loading } from './components.js';
32
+ export { RelipayBrowserClient, RelipayError } from './client.js';
33
+ export type { ReliPayBrowserConfig, EndUserDto } from './client.js';
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @relipay/react — React hooks + headless components for end-user auth.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // Server (Next.js App Router root layout)
7
+ * const user = await getCurrentUserFromCookie();
8
+ * return (
9
+ * <RelipayProvider apiUrl={env.RELIPAY_URL} initialUser={user} accessToken={user?.accessToken ?? null}>
10
+ * {children}
11
+ * </RelipayProvider>
12
+ * );
13
+ *
14
+ * // Client component
15
+ * import { useUser, SignedIn, SignedOut } from '@relipay/react';
16
+ *
17
+ * function Header() {
18
+ * const { user } = useUser();
19
+ * return (
20
+ * <>
21
+ * <SignedIn>Hi, {user!.email}</SignedIn>
22
+ * <SignedOut><a href="/sign-in">Sign in</a></SignedOut>
23
+ * </>
24
+ * );
25
+ * }
26
+ * ```
27
+ */
28
+ export { RelipayProvider } from './context.js';
29
+ export { useUser, useRelipay } from './hooks.js';
30
+ export { SignedIn, SignedOut, Loading } from './components.js';
31
+ export { RelipayBrowserClient, RelipayError } from './client.js';
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@relipay/react",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "ReliPay React hooks + components — useUser, SignedIn/SignedOut, sign-in / sign-up forms.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "AGENTS.md",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "react",
21
+ "hooks",
22
+ "auth",
23
+ "billing",
24
+ "relipay"
25
+ ],
26
+ "peerDependencies": {
27
+ "react": "^18.0.0 || ^19.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@relipay/shared-types": "0.1.0-beta.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.0.0",
34
+ "react": "^19.0.0",
35
+ "react-dom": "^19.0.0",
36
+ "@testing-library/react": "^16.1.0",
37
+ "@vitejs/plugin-react": "^4.3.4",
38
+ "jsdom": "^25.0.1",
39
+ "typescript": "^5.6.3",
40
+ "vitest": "^2.1.5"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "tag": "beta"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json",
48
+ "typecheck": "tsc -p tsconfig.json --noEmit",
49
+ "lint": "echo \"(eslint not yet wired)\"",
50
+ "test": "echo \"(react SDK tests deferred to panel/demo e2e)\""
51
+ }
52
+ }