@dashai/sdk 0.7.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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/auth/client.cjs +185 -0
  4. package/dist/auth/client.cjs.map +1 -0
  5. package/dist/auth/client.d.cts +137 -0
  6. package/dist/auth/client.d.ts +137 -0
  7. package/dist/auth/client.js +175 -0
  8. package/dist/auth/client.js.map +1 -0
  9. package/dist/auth/middleware.cjs +352 -0
  10. package/dist/auth/middleware.cjs.map +1 -0
  11. package/dist/auth/middleware.d.cts +90 -0
  12. package/dist/auth/middleware.d.ts +90 -0
  13. package/dist/auth/middleware.js +343 -0
  14. package/dist/auth/middleware.js.map +1 -0
  15. package/dist/auth/server.cjs +445 -0
  16. package/dist/auth/server.cjs.map +1 -0
  17. package/dist/auth/server.d.cts +170 -0
  18. package/dist/auth/server.d.ts +170 -0
  19. package/dist/auth/server.js +432 -0
  20. package/dist/auth/server.js.map +1 -0
  21. package/dist/auth/types.cjs +31 -0
  22. package/dist/auth/types.cjs.map +1 -0
  23. package/dist/auth/types.d.cts +163 -0
  24. package/dist/auth/types.d.ts +163 -0
  25. package/dist/auth/types.js +29 -0
  26. package/dist/auth/types.js.map +1 -0
  27. package/dist/deps/index.cjs +117 -0
  28. package/dist/deps/index.cjs.map +1 -0
  29. package/dist/deps/index.d.cts +93 -0
  30. package/dist/deps/index.d.ts +93 -0
  31. package/dist/deps/index.js +112 -0
  32. package/dist/deps/index.js.map +1 -0
  33. package/dist/errors-BV75u7b9.d.cts +207 -0
  34. package/dist/errors-BV75u7b9.d.ts +207 -0
  35. package/dist/index.cjs +721 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +171 -0
  38. package/dist/index.d.ts +171 -0
  39. package/dist/index.js +703 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/query/index.cjs +621 -0
  42. package/dist/query/index.cjs.map +1 -0
  43. package/dist/query/index.d.cts +800 -0
  44. package/dist/query/index.d.ts +800 -0
  45. package/dist/query/index.js +617 -0
  46. package/dist/query/index.js.map +1 -0
  47. package/dist/react/index.cjs +199 -0
  48. package/dist/react/index.cjs.map +1 -0
  49. package/dist/react/index.d.cts +117 -0
  50. package/dist/react/index.d.ts +117 -0
  51. package/dist/react/index.js +195 -0
  52. package/dist/react/index.js.map +1 -0
  53. package/dist/types-BLNQ1S1C.d.cts +396 -0
  54. package/dist/types-BLNQ1S1C.d.ts +396 -0
  55. package/package.json +109 -0
@@ -0,0 +1,170 @@
1
+ import { AuthOptions, ExchangeResult, RbacMeResponse, Session } from './types.cjs';
2
+
3
+ /**
4
+ * Server-side auth helpers — used from React Server Components, route
5
+ * handlers, and server actions. Imports `next/headers` lazily so the
6
+ * subpath remains importable from non-Next environments (tests, unit
7
+ * fixtures) without crashing on missing peer deps.
8
+ *
9
+ * Consumers:
10
+ * import { getServerSession, exchangeCode } from '@dashai/sdk/auth/server';
11
+ *
12
+ * Public surface:
13
+ * - `getServerSession(opts?)` — read the current session, or null
14
+ * - `exchangeCode(code, opts?)` — exchange an SSO one-time code for a session
15
+ * - `signOut(opts?)` — clear the session cookie + tell the backend
16
+ *
17
+ * All three accept an optional `AuthOptions` argument. Defaults come
18
+ * from `DASHWISE_*` env vars; see `config.ts`.
19
+ */
20
+
21
+ /**
22
+ * Get the current session, or `null` if the user isn't signed in.
23
+ *
24
+ * Reads the session cookie from `next/headers` and validates it
25
+ * against the DashWise backend at `/api/auth/sessions/me`. Returns
26
+ * the validated `Session` on success.
27
+ *
28
+ * Cache: this call is uncached on purpose. The backend's `/me`
29
+ * endpoint is cheap (single DB row by indexed PK). Consumers that
30
+ * call `getServerSession()` multiple times per request should
31
+ * memoize at the React Server Component level.
32
+ *
33
+ * @example
34
+ * import { getServerSession } from '@dashai/sdk/auth/server';
35
+ * import { redirect } from 'next/navigation';
36
+ *
37
+ * export default async function DashboardPage() {
38
+ * const session = await getServerSession();
39
+ * if (!session) redirect('/api/auth/sign-in');
40
+ * return <h1>Hi {session.user.email}</h1>;
41
+ * }
42
+ */
43
+ declare function getServerSession(options?: AuthOptions): Promise<Session | null>;
44
+ /**
45
+ * Fetch `/api/installations/:id/rbac/me` for the active session.
46
+ * Returns the resolved roles + permissions, or null on any failure
47
+ * (network, HTTP error, schema mismatch). Failure is non-fatal —
48
+ * the caller treats null as "no permissions" (default-deny).
49
+ *
50
+ * Exported so `auth/middleware.ts` can reuse the same code path
51
+ * without re-implementing the request shape.
52
+ */
53
+ declare function fetchRbacMe(apiBaseUrl: string, cookieHeader: string, installationId: number, fetchImpl: typeof globalThis.fetch): Promise<RbacMeResponse | null>;
54
+ /**
55
+ * Exchange an SSO one-time `code` (received at the OAuth callback
56
+ * URL `?code=...`) for a session. On success, sets the session
57
+ * cookie via `next/headers` AND returns the decoded session so the
58
+ * caller can chain a `redirect(returnTo)`.
59
+ *
60
+ * Call from your `/api/auth/callback/route.ts`:
61
+ *
62
+ * @example
63
+ * import { exchangeCode } from '@dashai/sdk/auth/server';
64
+ * import { NextResponse } from 'next/server';
65
+ *
66
+ * export async function GET(request: Request) {
67
+ * const url = new URL(request.url);
68
+ * const code = url.searchParams.get('code');
69
+ * const returnTo = url.searchParams.get('return_to') ?? '/';
70
+ * if (!code) return NextResponse.redirect(new URL('/', request.url));
71
+ * await exchangeCode(code); // sets the cookie
72
+ * return NextResponse.redirect(new URL(returnTo, request.url));
73
+ * }
74
+ *
75
+ * Throws `DashwiseError` with `code === 'INVALID_EXCHANGE_CODE'` if
76
+ * the backend rejects (expired / replay / forged code).
77
+ */
78
+ declare function exchangeCode(code: string, options?: AuthOptions): Promise<ExchangeResult>;
79
+ /**
80
+ * Sign out the current user: clears the session cookie and notifies
81
+ * the backend so the server-side session record can be revoked
82
+ * before its natural expiry.
83
+ *
84
+ * Safe to call when not signed in (no-op). Returns nothing.
85
+ *
86
+ * Backend-side revocation is best-effort: if `/sign-out` fails, the
87
+ * cookie is still cleared client-side, so the user's local session
88
+ * is over regardless. The backend will reap the orphaned session
89
+ * record on its next periodic sweep.
90
+ */
91
+ declare function signOut(options?: AuthOptions): Promise<void>;
92
+ /**
93
+ * Sync check: does the given session hold `permissionKey`?
94
+ *
95
+ * Accepts either the bare form (`tasks:delete`) or the namespaced
96
+ * form (`tasks-app:tasks:delete`). Returns false if the session is
97
+ * null OR has no RBAC data (default-deny floor).
98
+ *
99
+ * For consumers using `getServerSession()`, prefer the async variants
100
+ * below — they handle the case where you forgot to call
101
+ * `getServerSession()` first.
102
+ *
103
+ * @example
104
+ * const session = await getServerSession();
105
+ * if (!hasPermissionInSession(session, 'tasks:delete')) {
106
+ * return new Response('Forbidden', { status: 403 });
107
+ * }
108
+ */
109
+ declare function hasPermissionInSession(session: Session | null | undefined, permissionKey: string): boolean;
110
+ /**
111
+ * Sync check: does the given session hold `roleKey`?
112
+ * Same semantics as `hasPermissionInSession`.
113
+ */
114
+ declare function hasRoleInSession(session: Session | null | undefined, roleKey: string): boolean;
115
+ /**
116
+ * Async convenience: fetch the session AND check a permission in
117
+ * one call. Returns true iff a session exists + holds the key.
118
+ *
119
+ * Most server-component code paths look like:
120
+ *
121
+ * @example
122
+ * if (!(await hasPermission('tasks:delete'))) {
123
+ * return <Forbidden />;
124
+ * }
125
+ */
126
+ declare function hasPermission(permissionKey: string, options?: AuthOptions): Promise<boolean>;
127
+ /**
128
+ * Async convenience: as `hasPermission` but for roles.
129
+ */
130
+ declare function hasRole(roleKey: string, options?: AuthOptions): Promise<boolean>;
131
+ /**
132
+ * Throw `DashwiseError('PERMISSION_DENIED')` if the session lacks
133
+ * `permissionKey`. Pattern for guarding server actions + RSC
134
+ * branches that should redirect-to-403 on failure.
135
+ *
136
+ * Throws `RBAC_UNAVAILABLE` if there's no session at all — distinct
137
+ * from PERMISSION_DENIED so callers can branch on "needs sign-in"
138
+ * vs "signed in but forbidden".
139
+ *
140
+ * Returns the loaded session on success — saves a second
141
+ * `getServerSession()` call in handlers that need both.
142
+ *
143
+ * @example
144
+ * import { requirePermission } from '@dashai/sdk/auth/server';
145
+ *
146
+ * export default async function DeletePanel() {
147
+ * const session = await requirePermission('tasks:delete');
148
+ * return <DeleteForm userEmail={session.user.email} />;
149
+ * }
150
+ */
151
+ declare function requirePermission(permissionKey: string, options?: AuthOptions): Promise<Session>;
152
+ /**
153
+ * Throw `PERMISSION_DENIED` unless the session holds at least one of
154
+ * the requested permissions. Useful for endpoints with multiple
155
+ * acceptable scopes.
156
+ */
157
+ declare function requireAnyPermission(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
158
+ /**
159
+ * Throw `PERMISSION_DENIED` unless the session holds all of the
160
+ * requested permissions. Useful for multi-permission gating where
161
+ * partial grants aren't acceptable.
162
+ */
163
+ declare function requireAllPermissions(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
164
+ /**
165
+ * Throw `PERMISSION_DENIED` unless the session holds `roleKey`.
166
+ * Sugar over `requirePermission` for role-centric handlers.
167
+ */
168
+ declare function requireRole(roleKey: string, options?: AuthOptions): Promise<Session>;
169
+
170
+ export { exchangeCode, fetchRbacMe, getServerSession, hasPermission, hasPermissionInSession, hasRole, hasRoleInSession, requireAllPermissions, requireAnyPermission, requirePermission, requireRole, signOut };
@@ -0,0 +1,170 @@
1
+ import { AuthOptions, ExchangeResult, RbacMeResponse, Session } from './types.js';
2
+
3
+ /**
4
+ * Server-side auth helpers — used from React Server Components, route
5
+ * handlers, and server actions. Imports `next/headers` lazily so the
6
+ * subpath remains importable from non-Next environments (tests, unit
7
+ * fixtures) without crashing on missing peer deps.
8
+ *
9
+ * Consumers:
10
+ * import { getServerSession, exchangeCode } from '@dashai/sdk/auth/server';
11
+ *
12
+ * Public surface:
13
+ * - `getServerSession(opts?)` — read the current session, or null
14
+ * - `exchangeCode(code, opts?)` — exchange an SSO one-time code for a session
15
+ * - `signOut(opts?)` — clear the session cookie + tell the backend
16
+ *
17
+ * All three accept an optional `AuthOptions` argument. Defaults come
18
+ * from `DASHWISE_*` env vars; see `config.ts`.
19
+ */
20
+
21
+ /**
22
+ * Get the current session, or `null` if the user isn't signed in.
23
+ *
24
+ * Reads the session cookie from `next/headers` and validates it
25
+ * against the DashWise backend at `/api/auth/sessions/me`. Returns
26
+ * the validated `Session` on success.
27
+ *
28
+ * Cache: this call is uncached on purpose. The backend's `/me`
29
+ * endpoint is cheap (single DB row by indexed PK). Consumers that
30
+ * call `getServerSession()` multiple times per request should
31
+ * memoize at the React Server Component level.
32
+ *
33
+ * @example
34
+ * import { getServerSession } from '@dashai/sdk/auth/server';
35
+ * import { redirect } from 'next/navigation';
36
+ *
37
+ * export default async function DashboardPage() {
38
+ * const session = await getServerSession();
39
+ * if (!session) redirect('/api/auth/sign-in');
40
+ * return <h1>Hi {session.user.email}</h1>;
41
+ * }
42
+ */
43
+ declare function getServerSession(options?: AuthOptions): Promise<Session | null>;
44
+ /**
45
+ * Fetch `/api/installations/:id/rbac/me` for the active session.
46
+ * Returns the resolved roles + permissions, or null on any failure
47
+ * (network, HTTP error, schema mismatch). Failure is non-fatal —
48
+ * the caller treats null as "no permissions" (default-deny).
49
+ *
50
+ * Exported so `auth/middleware.ts` can reuse the same code path
51
+ * without re-implementing the request shape.
52
+ */
53
+ declare function fetchRbacMe(apiBaseUrl: string, cookieHeader: string, installationId: number, fetchImpl: typeof globalThis.fetch): Promise<RbacMeResponse | null>;
54
+ /**
55
+ * Exchange an SSO one-time `code` (received at the OAuth callback
56
+ * URL `?code=...`) for a session. On success, sets the session
57
+ * cookie via `next/headers` AND returns the decoded session so the
58
+ * caller can chain a `redirect(returnTo)`.
59
+ *
60
+ * Call from your `/api/auth/callback/route.ts`:
61
+ *
62
+ * @example
63
+ * import { exchangeCode } from '@dashai/sdk/auth/server';
64
+ * import { NextResponse } from 'next/server';
65
+ *
66
+ * export async function GET(request: Request) {
67
+ * const url = new URL(request.url);
68
+ * const code = url.searchParams.get('code');
69
+ * const returnTo = url.searchParams.get('return_to') ?? '/';
70
+ * if (!code) return NextResponse.redirect(new URL('/', request.url));
71
+ * await exchangeCode(code); // sets the cookie
72
+ * return NextResponse.redirect(new URL(returnTo, request.url));
73
+ * }
74
+ *
75
+ * Throws `DashwiseError` with `code === 'INVALID_EXCHANGE_CODE'` if
76
+ * the backend rejects (expired / replay / forged code).
77
+ */
78
+ declare function exchangeCode(code: string, options?: AuthOptions): Promise<ExchangeResult>;
79
+ /**
80
+ * Sign out the current user: clears the session cookie and notifies
81
+ * the backend so the server-side session record can be revoked
82
+ * before its natural expiry.
83
+ *
84
+ * Safe to call when not signed in (no-op). Returns nothing.
85
+ *
86
+ * Backend-side revocation is best-effort: if `/sign-out` fails, the
87
+ * cookie is still cleared client-side, so the user's local session
88
+ * is over regardless. The backend will reap the orphaned session
89
+ * record on its next periodic sweep.
90
+ */
91
+ declare function signOut(options?: AuthOptions): Promise<void>;
92
+ /**
93
+ * Sync check: does the given session hold `permissionKey`?
94
+ *
95
+ * Accepts either the bare form (`tasks:delete`) or the namespaced
96
+ * form (`tasks-app:tasks:delete`). Returns false if the session is
97
+ * null OR has no RBAC data (default-deny floor).
98
+ *
99
+ * For consumers using `getServerSession()`, prefer the async variants
100
+ * below — they handle the case where you forgot to call
101
+ * `getServerSession()` first.
102
+ *
103
+ * @example
104
+ * const session = await getServerSession();
105
+ * if (!hasPermissionInSession(session, 'tasks:delete')) {
106
+ * return new Response('Forbidden', { status: 403 });
107
+ * }
108
+ */
109
+ declare function hasPermissionInSession(session: Session | null | undefined, permissionKey: string): boolean;
110
+ /**
111
+ * Sync check: does the given session hold `roleKey`?
112
+ * Same semantics as `hasPermissionInSession`.
113
+ */
114
+ declare function hasRoleInSession(session: Session | null | undefined, roleKey: string): boolean;
115
+ /**
116
+ * Async convenience: fetch the session AND check a permission in
117
+ * one call. Returns true iff a session exists + holds the key.
118
+ *
119
+ * Most server-component code paths look like:
120
+ *
121
+ * @example
122
+ * if (!(await hasPermission('tasks:delete'))) {
123
+ * return <Forbidden />;
124
+ * }
125
+ */
126
+ declare function hasPermission(permissionKey: string, options?: AuthOptions): Promise<boolean>;
127
+ /**
128
+ * Async convenience: as `hasPermission` but for roles.
129
+ */
130
+ declare function hasRole(roleKey: string, options?: AuthOptions): Promise<boolean>;
131
+ /**
132
+ * Throw `DashwiseError('PERMISSION_DENIED')` if the session lacks
133
+ * `permissionKey`. Pattern for guarding server actions + RSC
134
+ * branches that should redirect-to-403 on failure.
135
+ *
136
+ * Throws `RBAC_UNAVAILABLE` if there's no session at all — distinct
137
+ * from PERMISSION_DENIED so callers can branch on "needs sign-in"
138
+ * vs "signed in but forbidden".
139
+ *
140
+ * Returns the loaded session on success — saves a second
141
+ * `getServerSession()` call in handlers that need both.
142
+ *
143
+ * @example
144
+ * import { requirePermission } from '@dashai/sdk/auth/server';
145
+ *
146
+ * export default async function DeletePanel() {
147
+ * const session = await requirePermission('tasks:delete');
148
+ * return <DeleteForm userEmail={session.user.email} />;
149
+ * }
150
+ */
151
+ declare function requirePermission(permissionKey: string, options?: AuthOptions): Promise<Session>;
152
+ /**
153
+ * Throw `PERMISSION_DENIED` unless the session holds at least one of
154
+ * the requested permissions. Useful for endpoints with multiple
155
+ * acceptable scopes.
156
+ */
157
+ declare function requireAnyPermission(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
158
+ /**
159
+ * Throw `PERMISSION_DENIED` unless the session holds all of the
160
+ * requested permissions. Useful for multi-permission gating where
161
+ * partial grants aren't acceptable.
162
+ */
163
+ declare function requireAllPermissions(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
164
+ /**
165
+ * Throw `PERMISSION_DENIED` unless the session holds `roleKey`.
166
+ * Sugar over `requirePermission` for role-centric handlers.
167
+ */
168
+ declare function requireRole(roleKey: string, options?: AuthOptions): Promise<Session>;
169
+
170
+ export { exchangeCode, fetchRbacMe, getServerSession, hasPermission, hasPermissionInSession, hasRole, hasRoleInSession, requireAllPermissions, requireAnyPermission, requirePermission, requireRole, signOut };