@mframework/adapter-betterauth 0.0.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/README.md +85 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +21 -0
- package/dist/src/auth.d.ts +2 -0
- package/dist/src/auth.js +31 -0
- package/dist/src/betterauth.d.ts +11 -0
- package/dist/src/betterauth.js +12 -0
- package/dist/src/config.d.ts +8 -0
- package/dist/src/config.js +12 -0
- package/dist/src/framework.d.ts +17 -0
- package/dist/src/framework.js +13 -0
- package/dist/src/plugins.d.ts +5 -0
- package/dist/src/plugins.js +19 -0
- package/dist/src/provider.d.ts +2 -0
- package/dist/src/provider.js +84 -0
- package/dist/src/registry.d.ts +4 -0
- package/dist/src/registry.js +21 -0
- package/dist/src/transport.d.ts +5 -0
- package/dist/src/transport.js +42 -0
- package/dist/src/types.d.ts +29 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +2 -0
- package/dist/src/utils.js +12 -0
- package/dist/src/validation.d.ts +22 -0
- package/dist/src/validation.js +21 -0
- package/index.ts +28 -0
- package/package.json +45 -0
- package/src/auth.ts +52 -0
- package/src/betterauth.ts +15 -0
- package/src/config.ts +22 -0
- package/src/framework.ts +24 -0
- package/src/plugins.ts +26 -0
- package/src/provider.ts +106 -0
- package/src/registry.ts +25 -0
- package/src/transport.ts +48 -0
- package/src/types-shims.d.ts +28 -0
- package/src/types.d.ts +13 -0
- package/src/types.ts +33 -0
- package/src/utils.ts +15 -0
- package/src/validation.ts +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Starter Adapter Template
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
This package is a template to scaffold new adapter packages that integrate with Meeovi layers.
|
|
5
|
+
|
|
6
|
+
Usage
|
|
7
|
+
-----
|
|
8
|
+
|
|
9
|
+
- From this package directory, run:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
npm run create -- <short-name>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- Example:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm run create -- shop
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This creates a new adapter package at `../adapter-shop` (relative to this template) with the package name `@mframework/adapter-shop`.
|
|
22
|
+
|
|
23
|
+
What you get
|
|
24
|
+
------------
|
|
25
|
+
|
|
26
|
+
- A ready-to-edit adapter package with `index.ts` and `src/` files for `transport`, `auth`, `commerce`, `search`, and `utils`.
|
|
27
|
+
- Placeholders in files are marked as `__PACKAGE_NAME__` and `__SHORT_NAME__` to help you customize.
|
|
28
|
+
|
|
29
|
+
How to edit
|
|
30
|
+
-----------
|
|
31
|
+
|
|
32
|
+
1. Open the new package folder.
|
|
33
|
+
2. Replace placeholder endpoints and logic in `src/*` with your adapter's API.
|
|
34
|
+
3. Update `package.json` fields as needed.
|
|
35
|
+
4. Run `npm run build` inside the new adapter package to compile TypeScript.
|
|
36
|
+
|
|
37
|
+
Notes
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
- The scaffold script performs simple text replacements; review generated files before publishing.
|
|
41
|
+
- This template is intentionally minimal — implement only the methods your backend supports.
|
|
42
|
+
|
|
43
|
+
CLI Flags
|
|
44
|
+
---------
|
|
45
|
+
|
|
46
|
+
The starter CLI supports interactive and non-interactive modes. Flags:
|
|
47
|
+
|
|
48
|
+
- `--name` / `-n`: Adapter short name (e.g. `shop`).
|
|
49
|
+
- `--desc` / `-d`: Description for the generated package.
|
|
50
|
+
- `--layers` / `-l`: Comma-separated list of layers to scaffold (e.g. `commerce,auth`).
|
|
51
|
+
- `--dest`: Destination path for the generated package.
|
|
52
|
+
- `--no-install`: Skip running `npm install` after scaffolding.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
|
|
57
|
+
- Interactive:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
npm run create:interactive
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Non-interactive (with install):
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
npm run create:interactive -- --name shop --desc "Shop adapter" --layers commerce,auth
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- Non-interactive (skip install):
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
npm run create:interactive -- --name shop --layers commerce --no-install
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Repo-level wrapper
|
|
76
|
+
------------------
|
|
77
|
+
|
|
78
|
+
From the repo root you can use the provided script:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
scripts/create-adapter --name shop --layers commerce --no-install
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
Note: adapter credentials and endpoints can be centrally configured in your main app's `.env` file. See the repository `.env.example` for recommended variable names.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const installAuthAdapter: (config: {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
}) => void;
|
|
5
|
+
export { auth, Auth } from './src/betterauth';
|
|
6
|
+
export { default as BetterAuthProvider } from './src/provider';
|
|
7
|
+
export { getAuthPlugins } from './src/plugins';
|
|
8
|
+
export * from './src/types';
|
|
9
|
+
export * from './src/auth';
|
|
10
|
+
export * from './src/utils';
|
|
11
|
+
export * from './src/transport';
|
|
12
|
+
export * from './src/framework';
|
|
13
|
+
export * from './src/registry';
|
|
14
|
+
export * from './src/validation';
|
|
15
|
+
export * from './src/config';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { setAuthAdapter } from '@mframework/sdk';
|
|
2
|
+
import { createAuthTransport } from './src/transport';
|
|
3
|
+
import { createAuthAdapter } from './src/auth';
|
|
4
|
+
export const installAuthAdapter = (config) => {
|
|
5
|
+
const transport = createAuthTransport(config);
|
|
6
|
+
setAuthAdapter(createAuthAdapter(transport));
|
|
7
|
+
};
|
|
8
|
+
// Export the server-side BetterAuth instance (if present) so other layers can
|
|
9
|
+
// import the runtime `auth` instance from this package. The file is optional
|
|
10
|
+
// and will only exist when BetterAuth is configured for the adapter.
|
|
11
|
+
export { auth } from './src/betterauth';
|
|
12
|
+
export { default as BetterAuthProvider } from './src/provider';
|
|
13
|
+
export { getAuthPlugins } from './src/plugins';
|
|
14
|
+
export * from './src/types';
|
|
15
|
+
export * from './src/auth';
|
|
16
|
+
export * from './src/utils';
|
|
17
|
+
export * from './src/transport';
|
|
18
|
+
export * from './src/framework';
|
|
19
|
+
export * from './src/registry';
|
|
20
|
+
export * from './src/validation';
|
|
21
|
+
export * from './src/config';
|
package/dist/src/auth.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { unwrap } from './utils';
|
|
2
|
+
export const createAuthAdapter = (transport) => ({
|
|
3
|
+
async login(input) {
|
|
4
|
+
const res = await transport.request('POST', '/login', {
|
|
5
|
+
body: input
|
|
6
|
+
});
|
|
7
|
+
return unwrap(res);
|
|
8
|
+
},
|
|
9
|
+
async register(input) {
|
|
10
|
+
const res = await transport.request('POST', '/register', {
|
|
11
|
+
body: input
|
|
12
|
+
});
|
|
13
|
+
return unwrap(res);
|
|
14
|
+
},
|
|
15
|
+
async logout() {
|
|
16
|
+
const res = await transport.request('POST', '/logout');
|
|
17
|
+
return unwrap({ ...res, data: true });
|
|
18
|
+
},
|
|
19
|
+
async getSession() {
|
|
20
|
+
const res = await transport.request('GET', '/session');
|
|
21
|
+
return unwrap(res);
|
|
22
|
+
},
|
|
23
|
+
async refresh() {
|
|
24
|
+
const res = await transport.request('POST', '/refresh');
|
|
25
|
+
return unwrap(res);
|
|
26
|
+
},
|
|
27
|
+
async getUser() {
|
|
28
|
+
const res = await transport.request('GET', '/user');
|
|
29
|
+
return unwrap(res);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
export declare const auth: import("better-auth").Auth<{
|
|
3
|
+
experimental: {
|
|
4
|
+
joins: true;
|
|
5
|
+
};
|
|
6
|
+
database: (options: import("better-auth").BetterAuthOptions) => import("better-auth").DBAdapter<import("better-auth").BetterAuthOptions>;
|
|
7
|
+
emailAndPassword: {
|
|
8
|
+
enabled: true;
|
|
9
|
+
};
|
|
10
|
+
}>;
|
|
11
|
+
export type Auth = typeof auth;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
4
|
+
import { prisma } from "@mframework/api";
|
|
5
|
+
// Create and export the BetterAuth runtime instance using the centralized
|
|
6
|
+
// Prisma client exported from packages/modules/api. Layers can import `auth`
|
|
7
|
+
// from this package to get the configured auth instance.
|
|
8
|
+
export const auth = betterAuth({
|
|
9
|
+
experimental: { joins: true },
|
|
10
|
+
database: prismaAdapter(prisma, { provider: 'postgresql' }),
|
|
11
|
+
emailAndPassword: { enabled: true }
|
|
12
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
let config = {
|
|
2
|
+
authProvider: 'better-auth',
|
|
3
|
+
authUrl: '',
|
|
4
|
+
sessionCookieName: 'session',
|
|
5
|
+
baseUrl: undefined
|
|
6
|
+
};
|
|
7
|
+
export function setAuthConfig(newConfig) {
|
|
8
|
+
config = { ...config, ...newConfig };
|
|
9
|
+
}
|
|
10
|
+
export function getAuthConfig() {
|
|
11
|
+
return config;
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface FrameworkContext {
|
|
2
|
+
getRequestURL?(): string;
|
|
3
|
+
getRequestHeaders?(): Record<string, string>;
|
|
4
|
+
getConfig?(): any;
|
|
5
|
+
reloadApp?(): void;
|
|
6
|
+
state?<T>(key: string, init: () => T): {
|
|
7
|
+
value: T;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Frameworks (Nuxt, React, etc.) call this once during initialization.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setFrameworkContext(newCtx: FrameworkContext): void;
|
|
14
|
+
/**
|
|
15
|
+
* Auth providers use this to access framework-specific helpers.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getFrameworkContext(): FrameworkContext;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
let ctx = {};
|
|
2
|
+
/**
|
|
3
|
+
* Frameworks (Nuxt, React, etc.) call this once during initialization.
|
|
4
|
+
*/
|
|
5
|
+
export function setFrameworkContext(newCtx) {
|
|
6
|
+
ctx = { ...ctx, ...newCtx };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Auth providers use this to access framework-specific helpers.
|
|
10
|
+
*/
|
|
11
|
+
export function getFrameworkContext() {
|
|
12
|
+
return ctx;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { stripeClient } from '@better-auth/stripe/client';
|
|
2
|
+
import { polarClient } from '@polar-sh/better-auth';
|
|
3
|
+
import { adminClient, inferAdditionalFields } from 'better-auth/client/plugins';
|
|
4
|
+
export function getAuthPlugins(opts = {}) {
|
|
5
|
+
const { subscription = true } = opts;
|
|
6
|
+
return [
|
|
7
|
+
inferAdditionalFields({
|
|
8
|
+
user: {
|
|
9
|
+
polarCustomerId: {
|
|
10
|
+
type: 'string'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}),
|
|
14
|
+
adminClient(),
|
|
15
|
+
polarClient(),
|
|
16
|
+
stripeClient({ subscription })
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
export default getAuthPlugins;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as BetterAuth from 'better-auth';
|
|
2
|
+
import { getAuthConfig } from './config';
|
|
3
|
+
import { getFrameworkContext } from './framework';
|
|
4
|
+
// Create a single shared Better Auth client instance
|
|
5
|
+
let client = null;
|
|
6
|
+
function resolveClientFactory() {
|
|
7
|
+
return BetterAuth.Client ?? BetterAuth.default ?? BetterAuth;
|
|
8
|
+
}
|
|
9
|
+
function getClient() {
|
|
10
|
+
if (client)
|
|
11
|
+
return client;
|
|
12
|
+
const config = getAuthConfig();
|
|
13
|
+
const ctx = getFrameworkContext();
|
|
14
|
+
const Client = resolveClientFactory();
|
|
15
|
+
client = Client({
|
|
16
|
+
baseURL: config.baseUrl,
|
|
17
|
+
fetch: async (url, options = {}) => {
|
|
18
|
+
const baseHeaders = ctx.getRequestHeaders?.() || {};
|
|
19
|
+
let optionHeaders = {};
|
|
20
|
+
if (options.headers instanceof Headers) {
|
|
21
|
+
options.headers.forEach((value, key) => {
|
|
22
|
+
optionHeaders[key] = value;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else if (Array.isArray(options.headers)) {
|
|
26
|
+
for (const [k, v] of options.headers) {
|
|
27
|
+
optionHeaders[k] = v;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (typeof options.headers === 'object' && options.headers !== null) {
|
|
31
|
+
optionHeaders = options.headers;
|
|
32
|
+
}
|
|
33
|
+
const headers = {
|
|
34
|
+
...baseHeaders,
|
|
35
|
+
...optionHeaders
|
|
36
|
+
};
|
|
37
|
+
return fetch(url, { ...options, headers });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return client;
|
|
41
|
+
}
|
|
42
|
+
const BetterAuthProvider = {
|
|
43
|
+
async login(credentials) {
|
|
44
|
+
const client = getClient();
|
|
45
|
+
const result = await client.signIn(credentials);
|
|
46
|
+
if (result.error) {
|
|
47
|
+
throw new Error(result.error.message || 'Login failed');
|
|
48
|
+
}
|
|
49
|
+
return result.data;
|
|
50
|
+
},
|
|
51
|
+
async logout() {
|
|
52
|
+
const client = getClient();
|
|
53
|
+
await client.signOut();
|
|
54
|
+
const ctx = getFrameworkContext();
|
|
55
|
+
ctx.reloadApp?.();
|
|
56
|
+
},
|
|
57
|
+
async session() {
|
|
58
|
+
const client = getClient();
|
|
59
|
+
const result = await client.session();
|
|
60
|
+
if (result.error) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return result.data;
|
|
64
|
+
},
|
|
65
|
+
async register(data) {
|
|
66
|
+
const client = getClient();
|
|
67
|
+
const result = await client.signUp(data);
|
|
68
|
+
if (result.error) {
|
|
69
|
+
throw new Error(result.error.message || 'Registration failed');
|
|
70
|
+
}
|
|
71
|
+
return result.data;
|
|
72
|
+
},
|
|
73
|
+
async refresh() {
|
|
74
|
+
const client = getClient();
|
|
75
|
+
const result = await client.refresh();
|
|
76
|
+
if (result.error) {
|
|
77
|
+
throw new Error(result.error.message || 'Session refresh failed');
|
|
78
|
+
}
|
|
79
|
+
return result.data;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
// Register the provider under the canonical name so other layers can discover
|
|
83
|
+
// it using the existing registry mechanism.
|
|
84
|
+
export default BetterAuthProvider;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// allow missing entries in the map so TypeScript understands runtime checks
|
|
2
|
+
const providers = {};
|
|
3
|
+
let activeProvider = null;
|
|
4
|
+
export function registerAuthProvider(name, provider) {
|
|
5
|
+
providers[name] = provider;
|
|
6
|
+
}
|
|
7
|
+
export function setActiveAuthProvider(name) {
|
|
8
|
+
if (!providers[name]) {
|
|
9
|
+
throw new Error(`Auth provider "${name}" is not registered`);
|
|
10
|
+
}
|
|
11
|
+
activeProvider = name;
|
|
12
|
+
}
|
|
13
|
+
export function getAuthProvider() {
|
|
14
|
+
if (!activeProvider) {
|
|
15
|
+
throw new Error('No active auth provider has been set');
|
|
16
|
+
}
|
|
17
|
+
const prov = providers[activeProvider];
|
|
18
|
+
if (!prov)
|
|
19
|
+
throw new Error(`Auth provider "${activeProvider}" not found`);
|
|
20
|
+
return prov;
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const createAuthTransport = (config) => {
|
|
2
|
+
return {
|
|
3
|
+
async request(method, path, options = {}) {
|
|
4
|
+
try {
|
|
5
|
+
const url = new URL(path, config.baseUrl);
|
|
6
|
+
if (options.query) {
|
|
7
|
+
Object.entries(options.query).forEach(([key, value]) => {
|
|
8
|
+
url.searchParams.set(key, String(value));
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
const res = await fetch(url.toString(), {
|
|
12
|
+
method,
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
16
|
+
...(options.headers || {})
|
|
17
|
+
},
|
|
18
|
+
body: options.body ? JSON.stringify(options.body) : undefined
|
|
19
|
+
});
|
|
20
|
+
const data = await res.json().catch(() => null);
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
return {
|
|
23
|
+
status: res.status,
|
|
24
|
+
data: null,
|
|
25
|
+
error: data?.message || 'Unknown error'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
status: res.status,
|
|
30
|
+
data
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
return {
|
|
35
|
+
status: 500,
|
|
36
|
+
data: null,
|
|
37
|
+
error: err.message || 'Transport error'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface AuthSession {
|
|
2
|
+
user: {
|
|
3
|
+
id: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
avatarUrl?: string;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
};
|
|
9
|
+
expiresAt?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
export interface AuthCredentials {
|
|
13
|
+
email: string;
|
|
14
|
+
password: string;
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
export interface AuthRegistration {
|
|
18
|
+
email: string;
|
|
19
|
+
password: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthProvider {
|
|
24
|
+
login(credentials: AuthCredentials): Promise<AuthSession>;
|
|
25
|
+
logout(): Promise<void>;
|
|
26
|
+
session(): Promise<AuthSession | null>;
|
|
27
|
+
register?(data: AuthRegistration): Promise<AuthSession>;
|
|
28
|
+
refresh?(): Promise<AuthSession>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const loginSchema: z.ZodObject<{
|
|
3
|
+
email: z.ZodString;
|
|
4
|
+
password: z.ZodString;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export type LoginInput = z.infer<typeof loginSchema>;
|
|
7
|
+
export declare const registerSchema: z.ZodObject<{
|
|
8
|
+
email: z.ZodString;
|
|
9
|
+
password: z.ZodString;
|
|
10
|
+
confirmPassword: z.ZodString;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type RegisterInput = z.infer<typeof registerSchema>;
|
|
13
|
+
export declare const forgotPasswordSchema: z.ZodObject<{
|
|
14
|
+
email: z.ZodString;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>;
|
|
17
|
+
export declare const resetPasswordSchema: z.ZodObject<{
|
|
18
|
+
token: z.ZodString;
|
|
19
|
+
password: z.ZodString;
|
|
20
|
+
confirmPassword: z.ZodString;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const loginSchema = z.object({
|
|
3
|
+
email: z.string().email(),
|
|
4
|
+
password: z.string().min(8)
|
|
5
|
+
});
|
|
6
|
+
export const registerSchema = z.object({
|
|
7
|
+
email: z.string().email(),
|
|
8
|
+
password: z.string().min(8),
|
|
9
|
+
confirmPassword: z.string().min(8)
|
|
10
|
+
}).refine((data) => data.password === data.confirmPassword, {
|
|
11
|
+
message: 'Passwords do not match',
|
|
12
|
+
path: ['confirmPassword']
|
|
13
|
+
});
|
|
14
|
+
export const forgotPasswordSchema = z.object({
|
|
15
|
+
email: z.string().email()
|
|
16
|
+
});
|
|
17
|
+
export const resetPasswordSchema = z.object({
|
|
18
|
+
token: z.string(),
|
|
19
|
+
password: z.string().min(8),
|
|
20
|
+
confirmPassword: z.string().min(8)
|
|
21
|
+
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setAuthAdapter
|
|
3
|
+
} from '@mframework/sdk'
|
|
4
|
+
|
|
5
|
+
import { createAuthTransport } from './src/transport'
|
|
6
|
+
import { createAuthAdapter } from './src/auth'
|
|
7
|
+
|
|
8
|
+
export const installAuthAdapter = (config: { baseUrl: string; apiKey?: string }) => {
|
|
9
|
+
const transport = createAuthTransport(config)
|
|
10
|
+
|
|
11
|
+
setAuthAdapter(createAuthAdapter(transport))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Export the server-side BetterAuth instance (if present) so other layers can
|
|
15
|
+
// import the runtime `auth` instance from this package. The file is optional
|
|
16
|
+
// and will only exist when BetterAuth is configured for the adapter.
|
|
17
|
+
export { auth, Auth } from './src/betterauth'
|
|
18
|
+
export { default as BetterAuthProvider } from './src/provider'
|
|
19
|
+
export { getAuthPlugins } from './src/plugins'
|
|
20
|
+
|
|
21
|
+
export * from './src/types'
|
|
22
|
+
export * from './src/auth'
|
|
23
|
+
export * from './src/utils'
|
|
24
|
+
export * from './src/transport'
|
|
25
|
+
export * from './src/framework'
|
|
26
|
+
export * from './src/registry'
|
|
27
|
+
export * from './src/validation'
|
|
28
|
+
export * from './src/config'
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mframework/adapter-betterauth",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Official Better-Auth adapter for M Framework Auth Layer",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"types": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./src/index.ts",
|
|
10
|
+
"types": "./src/types.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"build": "tsc -p tsconfig.json",
|
|
16
|
+
"create": "node ./scripts/init-adapter.js",
|
|
17
|
+
"create:interactive": "node ./scripts/create-adapter-cli.cjs"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"meeovi",
|
|
21
|
+
"adapter",
|
|
22
|
+
"starter"
|
|
23
|
+
],
|
|
24
|
+
"author": "M Framework",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@better-auth/cli": "^1.4.10",
|
|
29
|
+
"@better-auth/oauth-provider": "^1.4.12",
|
|
30
|
+
"@better-auth/passkey": "^1.4.10",
|
|
31
|
+
"@better-auth/scim": "^1.4.15",
|
|
32
|
+
"@better-auth/sso": "^1.4.12",
|
|
33
|
+
"@better-auth/stripe": "^1.4.15",
|
|
34
|
+
"@mframework/core": "^0.0.1",
|
|
35
|
+
"@mframework/sdk": "^0.0.2",
|
|
36
|
+
"@polar-sh/better-auth": "^1.6.3",
|
|
37
|
+
"better-auth": "^1.4.12",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"src",
|
|
43
|
+
"README.md"
|
|
44
|
+
]
|
|
45
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthAdapter,
|
|
3
|
+
TransportAdapter
|
|
4
|
+
} from '@mframework/sdk'
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
LoginInput,
|
|
8
|
+
RegisterInput,
|
|
9
|
+
Result,
|
|
10
|
+
Session,
|
|
11
|
+
User
|
|
12
|
+
} from '@mframework/core'
|
|
13
|
+
|
|
14
|
+
import { unwrap } from './utils'
|
|
15
|
+
|
|
16
|
+
export const createAuthAdapter = (
|
|
17
|
+
transport: TransportAdapter
|
|
18
|
+
): AuthAdapter => ({
|
|
19
|
+
async login(input: LoginInput): Promise<Result<Session>> {
|
|
20
|
+
const res = await transport.request<Session>('POST', '/login', {
|
|
21
|
+
body: input
|
|
22
|
+
})
|
|
23
|
+
return unwrap(res)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
async register(input: RegisterInput): Promise<Result<Session>> {
|
|
27
|
+
const res = await transport.request<Session>('POST', '/register', {
|
|
28
|
+
body: input
|
|
29
|
+
})
|
|
30
|
+
return unwrap(res)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async logout(): Promise<Result<true>> {
|
|
34
|
+
const res = await transport.request('POST', '/logout')
|
|
35
|
+
return unwrap({ ...res, data: true })
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async getSession(): Promise<Result<Session>> {
|
|
39
|
+
const res = await transport.request<Session>('GET', '/session')
|
|
40
|
+
return unwrap(res)
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async refresh(): Promise<Result<Session>> {
|
|
44
|
+
const res = await transport.request<Session>('POST', '/refresh')
|
|
45
|
+
return unwrap(res)
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async getUser(): Promise<Result<User>> {
|
|
49
|
+
const res = await transport.request<User>('GET', '/user')
|
|
50
|
+
return unwrap(res)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "dotenv/config"
|
|
2
|
+
import { betterAuth } from "better-auth"
|
|
3
|
+
import { prismaAdapter } from "better-auth/adapters/prisma"
|
|
4
|
+
import { prisma } from "@mframework/api"
|
|
5
|
+
|
|
6
|
+
// Create and export the BetterAuth runtime instance using the centralized
|
|
7
|
+
// Prisma client exported from packages/modules/api. Layers can import `auth`
|
|
8
|
+
// from this package to get the configured auth instance.
|
|
9
|
+
export const auth = betterAuth({
|
|
10
|
+
experimental: { joins: true },
|
|
11
|
+
database: prismaAdapter(prisma as any, { provider: 'postgresql' }),
|
|
12
|
+
emailAndPassword: { enabled: true }
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export type Auth = typeof auth
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
export interface AuthConfig {
|
|
3
|
+
baseUrl: any
|
|
4
|
+
authProvider: string
|
|
5
|
+
authUrl?: string
|
|
6
|
+
sessionCookieName?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let config: AuthConfig = {
|
|
10
|
+
authProvider: 'better-auth',
|
|
11
|
+
authUrl: '',
|
|
12
|
+
sessionCookieName: 'session',
|
|
13
|
+
baseUrl: undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function setAuthConfig(newConfig: Partial<AuthConfig>) {
|
|
17
|
+
config = { ...config, ...newConfig }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getAuthConfig(): AuthConfig {
|
|
21
|
+
return config
|
|
22
|
+
}
|
package/src/framework.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
export interface FrameworkContext {
|
|
3
|
+
getRequestURL?(): string
|
|
4
|
+
getRequestHeaders?(): Record<string, string>
|
|
5
|
+
getConfig?(): any
|
|
6
|
+
reloadApp?(): void
|
|
7
|
+
state?<T>(key: string, init: () => T): { value: T }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let ctx: FrameworkContext = {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Frameworks (Nuxt, React, etc.) call this once during initialization.
|
|
14
|
+
*/
|
|
15
|
+
export function setFrameworkContext(newCtx: FrameworkContext) {
|
|
16
|
+
ctx = { ...ctx, ...newCtx }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Auth providers use this to access framework-specific helpers.
|
|
21
|
+
*/
|
|
22
|
+
export function getFrameworkContext(): FrameworkContext {
|
|
23
|
+
return ctx
|
|
24
|
+
}
|
package/src/plugins.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { stripeClient } from '@better-auth/stripe/client'
|
|
2
|
+
import { polarClient } from '@polar-sh/better-auth'
|
|
3
|
+
import { adminClient, inferAdditionalFields } from 'better-auth/client/plugins'
|
|
4
|
+
|
|
5
|
+
export type AuthPluginOptions = {
|
|
6
|
+
subscription?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getAuthPlugins(opts: AuthPluginOptions = {}): any[] {
|
|
10
|
+
const { subscription = true } = opts
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
inferAdditionalFields({
|
|
14
|
+
user: {
|
|
15
|
+
polarCustomerId: {
|
|
16
|
+
type: 'string'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
adminClient(),
|
|
21
|
+
polarClient(),
|
|
22
|
+
stripeClient({ subscription })
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default getAuthPlugins
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as BetterAuth from 'better-auth'
|
|
2
|
+
import { getAuthConfig } from './config'
|
|
3
|
+
import { getFrameworkContext } from './framework'
|
|
4
|
+
|
|
5
|
+
// Create a single shared Better Auth client instance
|
|
6
|
+
let client: any = null
|
|
7
|
+
|
|
8
|
+
function resolveClientFactory() {
|
|
9
|
+
return (BetterAuth as any).Client ?? (BetterAuth as any).default ?? BetterAuth
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getClient() {
|
|
13
|
+
if (client) return client
|
|
14
|
+
|
|
15
|
+
const config = getAuthConfig()
|
|
16
|
+
const ctx = getFrameworkContext()
|
|
17
|
+
|
|
18
|
+
const Client = resolveClientFactory()
|
|
19
|
+
client = Client({
|
|
20
|
+
baseURL: config.baseUrl,
|
|
21
|
+
fetch: async (url: string | Request | URL, options: RequestInit = {}) => {
|
|
22
|
+
const baseHeaders = ctx.getRequestHeaders?.() || {}
|
|
23
|
+
|
|
24
|
+
let optionHeaders: Record<string, string> = {}
|
|
25
|
+
|
|
26
|
+
if (options.headers instanceof Headers) {
|
|
27
|
+
options.headers.forEach((value, key) => {
|
|
28
|
+
optionHeaders[key] = value
|
|
29
|
+
})
|
|
30
|
+
} else if (Array.isArray(options.headers)) {
|
|
31
|
+
for (const [k, v] of options.headers) {
|
|
32
|
+
optionHeaders[k] = v
|
|
33
|
+
}
|
|
34
|
+
} else if (typeof options.headers === 'object' && options.headers !== null) {
|
|
35
|
+
optionHeaders = options.headers as Record<string, string>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const headers = {
|
|
39
|
+
...baseHeaders,
|
|
40
|
+
...optionHeaders
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return fetch(url, { ...options, headers })
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return client
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const BetterAuthProvider: any = {
|
|
51
|
+
async login(credentials: any) {
|
|
52
|
+
const client = getClient()
|
|
53
|
+
const result = await client.signIn(credentials)
|
|
54
|
+
|
|
55
|
+
if (result.error) {
|
|
56
|
+
throw new Error(result.error.message || 'Login failed')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result.data
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
async logout() {
|
|
63
|
+
const client = getClient()
|
|
64
|
+
await client.signOut()
|
|
65
|
+
|
|
66
|
+
const ctx = getFrameworkContext()
|
|
67
|
+
ctx.reloadApp?.()
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
async session() {
|
|
71
|
+
const client = getClient()
|
|
72
|
+
const result = await client.session()
|
|
73
|
+
|
|
74
|
+
if (result.error) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return result.data
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async register(data: any) {
|
|
82
|
+
const client = getClient()
|
|
83
|
+
const result = await client.signUp(data)
|
|
84
|
+
|
|
85
|
+
if (result.error) {
|
|
86
|
+
throw new Error(result.error.message || 'Registration failed')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result.data
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async refresh() {
|
|
93
|
+
const client = getClient()
|
|
94
|
+
const result = await client.refresh()
|
|
95
|
+
|
|
96
|
+
if (result.error) {
|
|
97
|
+
throw new Error(result.error.message || 'Session refresh failed')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result.data
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Register the provider under the canonical name so other layers can discover
|
|
105
|
+
// it using the existing registry mechanism.
|
|
106
|
+
export default BetterAuthProvider
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuthProvider } from './types'
|
|
2
|
+
|
|
3
|
+
// allow missing entries in the map so TypeScript understands runtime checks
|
|
4
|
+
const providers: Record<string, AuthProvider | undefined> = {}
|
|
5
|
+
let activeProvider: any | null = null
|
|
6
|
+
|
|
7
|
+
export function registerAuthProvider(name: string, provider: AuthProvider) {
|
|
8
|
+
providers[name] = provider
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function setActiveAuthProvider(name: string) {
|
|
12
|
+
if (!providers[name]) {
|
|
13
|
+
throw new Error(`Auth provider "${name}" is not registered`)
|
|
14
|
+
}
|
|
15
|
+
activeProvider = name
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getAuthProvider(): AuthProvider {
|
|
19
|
+
if (!activeProvider) {
|
|
20
|
+
throw new Error('No active auth provider has been set')
|
|
21
|
+
}
|
|
22
|
+
const prov = providers[activeProvider!]
|
|
23
|
+
if (!prov) throw new Error(`Auth provider "${activeProvider}" not found`)
|
|
24
|
+
return prov
|
|
25
|
+
}
|
package/src/transport.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { TransportAdapter, RequestOptions, APIResponse } from '@mframework/core'
|
|
2
|
+
|
|
3
|
+
export const createAuthTransport = (config: { baseUrl: string; apiKey?: string }): TransportAdapter => {
|
|
4
|
+
return {
|
|
5
|
+
async request<T>(method: any, path: string | URL, options: RequestOptions = {}): Promise<APIResponse<T>> {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(path, config.baseUrl)
|
|
8
|
+
|
|
9
|
+
if (options.query) {
|
|
10
|
+
Object.entries(options.query).forEach(([key, value]) => {
|
|
11
|
+
url.searchParams.set(key, String(value))
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const res = await fetch(url.toString(), {
|
|
16
|
+
method,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
20
|
+
...(options.headers || {})
|
|
21
|
+
},
|
|
22
|
+
body: options.body ? JSON.stringify(options.body) : undefined
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const data = await res.json().catch(() => null)
|
|
26
|
+
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
return {
|
|
29
|
+
status: res.status,
|
|
30
|
+
data: (null as any) as T,
|
|
31
|
+
error: data?.message || 'Unknown error'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
status: res.status,
|
|
37
|
+
data
|
|
38
|
+
}
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
return {
|
|
41
|
+
status: 500,
|
|
42
|
+
data: (null as any) as T,
|
|
43
|
+
error: err.message || 'Transport error'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare module '@mframework/sdk' {
|
|
2
|
+
export type TransportAdapter = {
|
|
3
|
+
request<T = any>(method: string, path: string, opts?: any): Promise<any>
|
|
4
|
+
}
|
|
5
|
+
export type AuthAdapter = any
|
|
6
|
+
export type CommerceAdapter = any
|
|
7
|
+
export type SearchAdapter = any
|
|
8
|
+
export function setAuthAdapter(adapter: AuthAdapter): void
|
|
9
|
+
export function setCommerceAdapter(adapter: CommerceAdapter): void
|
|
10
|
+
export function setSearchAdapter(adapter: SearchAdapter): void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module '@mframework/core' {
|
|
14
|
+
export type LoginInput = any
|
|
15
|
+
export type RegisterInput = any
|
|
16
|
+
export type Result<T = any> = any
|
|
17
|
+
export type Session = any
|
|
18
|
+
export type User = any
|
|
19
|
+
export type TransportAdapter = import('@mframework/sdk').TransportAdapter
|
|
20
|
+
export type RequestOptions = any
|
|
21
|
+
export type APIResponse<T = any> = any
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module '@mframework/api' {
|
|
25
|
+
export const prisma: any
|
|
26
|
+
export function useDB(_event?: any): Promise<any>
|
|
27
|
+
export function isValidTable(name: string): boolean
|
|
28
|
+
}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Re-export the canonical SDK/core types so consumers of this adapter can import
|
|
2
|
+
// from the adapter package and still get accurate types from the source
|
|
3
|
+
// modules.
|
|
4
|
+
export type { TransportAdapter, AuthAdapter, CommerceAdapter, SearchAdapter } from '@mframework/sdk'
|
|
5
|
+
export type { LoginInput, RegisterInput, Result, Session, User } from '@mframework/core'
|
|
6
|
+
|
|
7
|
+
// Re-export prisma utilities from the api package for convenience.
|
|
8
|
+
export { prisma, useDB, isValidTable } from '@mframework/api'
|
|
9
|
+
|
|
10
|
+
// Adapter exports
|
|
11
|
+
export function getAuthPlugins(opts?: any): any[]
|
|
12
|
+
export const BetterAuthProvider: any
|
|
13
|
+
export default BetterAuthProvider
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
export interface AuthSession {
|
|
3
|
+
user: {
|
|
4
|
+
id: string
|
|
5
|
+
email?: string
|
|
6
|
+
name?: string
|
|
7
|
+
avatarUrl?: string
|
|
8
|
+
[key: string]: any
|
|
9
|
+
}
|
|
10
|
+
expiresAt?: string
|
|
11
|
+
[key: string]: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AuthCredentials {
|
|
15
|
+
email: string
|
|
16
|
+
password: string
|
|
17
|
+
[key: string]: any
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AuthRegistration {
|
|
21
|
+
email: string
|
|
22
|
+
password: string
|
|
23
|
+
name?: string
|
|
24
|
+
[key: string]: any
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AuthProvider {
|
|
28
|
+
login(credentials: AuthCredentials): Promise<AuthSession>
|
|
29
|
+
logout(): Promise<void>
|
|
30
|
+
session(): Promise<AuthSession | null>
|
|
31
|
+
register?(data: AuthRegistration): Promise<AuthSession>
|
|
32
|
+
refresh?(): Promise<AuthSession>
|
|
33
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { APIResponse, Result } from '@mframework/core'
|
|
2
|
+
|
|
3
|
+
export const unwrap = <T>(response: APIResponse<T>): Result<T> => {
|
|
4
|
+
if (response.error) {
|
|
5
|
+
return {
|
|
6
|
+
ok: false,
|
|
7
|
+
error: response.error
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
ok: true,
|
|
13
|
+
data: response.data
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const loginSchema = z.object({
|
|
4
|
+
email: z.string().email(),
|
|
5
|
+
password: z.string().min(8)
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export type LoginInput = z.infer<typeof loginSchema>
|
|
9
|
+
|
|
10
|
+
export const registerSchema = z.object({
|
|
11
|
+
email: z.string().email(),
|
|
12
|
+
password: z.string().min(8),
|
|
13
|
+
confirmPassword: z.string().min(8)
|
|
14
|
+
}).refine((data) => data.password === data.confirmPassword, {
|
|
15
|
+
message: 'Passwords do not match',
|
|
16
|
+
path: ['confirmPassword']
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export type RegisterInput = z.infer<typeof registerSchema>
|
|
20
|
+
|
|
21
|
+
export const forgotPasswordSchema = z.object({
|
|
22
|
+
email: z.string().email()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>
|
|
26
|
+
|
|
27
|
+
export const resetPasswordSchema = z.object({
|
|
28
|
+
token: z.string(),
|
|
29
|
+
password: z.string().min(8),
|
|
30
|
+
confirmPassword: z.string().min(8)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>
|