better-auth-lead 0.3.0 → 0.4.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 +55 -22
- package/dist/client.d.mts +8 -10
- package/dist/client.mjs +1 -3
- package/dist/client.mjs.map +1 -1
- package/dist/{index-B7NvMYsP.d.mts → index-BKKjl5VY.d.mts} +53 -29
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +19 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/dist/error-codes-CZUPEOrE.mjs +0 -12
- package/dist/error-codes-CZUPEOrE.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ To enable email verification, you need to pass a function that sends a verificat
|
|
|
113
113
|
- `lead`: The lead object.
|
|
114
114
|
- `url`: The URL to send to the user which contains the token.
|
|
115
115
|
- `token`: A verification token used to complete the email verification.
|
|
116
|
+
- `unsubscribeUrl`: The endpoint URL for one-click unsubscribe (RFC 8058). Use this in `List-Unsubscribe` email headers.
|
|
116
117
|
|
|
117
118
|
and a `request` object as the second parameter.
|
|
118
119
|
|
|
@@ -125,7 +126,7 @@ import { sendEmail } from './email'; // your email sending function
|
|
|
125
126
|
export const auth = betterAuth({
|
|
126
127
|
plugins: [
|
|
127
128
|
lead({
|
|
128
|
-
sendVerificationEmail: async ({ lead, url, token }) => {
|
|
129
|
+
sendVerificationEmail: async ({ lead, url, token, unsubscribeUrl }) => {
|
|
129
130
|
const { verificationEmailSentAt } = lead;
|
|
130
131
|
if (
|
|
131
132
|
verificationEmailSentAt &&
|
|
@@ -141,6 +142,12 @@ export const auth = betterAuth({
|
|
|
141
142
|
to: lead.email,
|
|
142
143
|
subject: 'Newsletter: Verify your email address',
|
|
143
144
|
text: `Click the link to verify your email: ${url}`,
|
|
145
|
+
// One-click unsubscribe headers (RFC 8058)
|
|
146
|
+
// Supported by Gmail, Apple Mail, and Yahoo Mail.
|
|
147
|
+
headers: {
|
|
148
|
+
'List-Unsubscribe': `<${unsubscribeUrl}>`,
|
|
149
|
+
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
|
|
150
|
+
},
|
|
144
151
|
});
|
|
145
152
|
|
|
146
153
|
return true;
|
|
@@ -160,30 +167,54 @@ Additionally, you can provide an `onEmailVerified` callback to execute logic aft
|
|
|
160
167
|
|
|
161
168
|
### Metadata Validation
|
|
162
169
|
|
|
163
|
-
To validate and parse metadata,
|
|
170
|
+
To validate and parse metadata, pass a Standard Schema compatible schema (e.g. Zod, Valibot, ArkType) to the `metadata.validationSchema` option. If validation fails, `subscribe` and `update` return a `400 Bad Request` with `INVALID_METADATA`.
|
|
171
|
+
|
|
172
|
+
To share the type with the client without bundling server code, define the schema in a shared file and import only the type on the client side:
|
|
164
173
|
|
|
165
174
|
```ts
|
|
166
|
-
//
|
|
167
|
-
import { betterAuth } from 'better-auth';
|
|
168
|
-
import { lead } from 'better-auth-lead';
|
|
175
|
+
// shared/lead-metadata-schema.ts
|
|
169
176
|
import * as z from 'zod';
|
|
170
177
|
|
|
171
|
-
const
|
|
178
|
+
export const leadMetadataSchema = z.object({
|
|
172
179
|
preferences: z.enum(['engineering', 'marketing', 'design']),
|
|
173
180
|
});
|
|
174
181
|
|
|
182
|
+
export type LeadMetadata = z.infer<typeof leadMetadataSchema>;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
// server/auth.ts
|
|
187
|
+
import { betterAuth } from 'better-auth';
|
|
188
|
+
import { lead } from 'better-auth-lead';
|
|
189
|
+
import { leadMetadataSchema } from './shared/lead-metadata-schema';
|
|
190
|
+
|
|
175
191
|
export const auth = betterAuth({
|
|
176
192
|
plugins: [
|
|
177
193
|
lead({
|
|
178
194
|
metadata: {
|
|
179
|
-
validationSchema:
|
|
195
|
+
validationSchema: leadMetadataSchema,
|
|
180
196
|
},
|
|
181
197
|
}),
|
|
182
198
|
],
|
|
183
199
|
});
|
|
184
200
|
```
|
|
185
201
|
|
|
186
|
-
|
|
202
|
+
```ts
|
|
203
|
+
// client/auth-client.ts
|
|
204
|
+
import { createAuthClient } from 'better-auth/client';
|
|
205
|
+
import { leadClient } from 'better-auth-lead/client';
|
|
206
|
+
import type { LeadMetadata } from './shared/lead-metadata-schema';
|
|
207
|
+
|
|
208
|
+
const authClient = createAuthClient({
|
|
209
|
+
plugins: [leadClient<LeadMetadata>()],
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// metadata is now typed as LeadMetadata
|
|
213
|
+
await authClient.lead.subscribe({
|
|
214
|
+
email: 'user@example.com',
|
|
215
|
+
metadata: { preferences: 'engineering' },
|
|
216
|
+
});
|
|
217
|
+
```
|
|
187
218
|
|
|
188
219
|
## Schema
|
|
189
220
|
|
|
@@ -191,25 +222,27 @@ If the schema validation fails, the API `subscribe` and `update` routes will ret
|
|
|
191
222
|
|
|
192
223
|
Table name: `lead`
|
|
193
224
|
|
|
194
|
-
| Field
|
|
195
|
-
|
|
|
196
|
-
| id
|
|
197
|
-
| email
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
225
|
+
| Field | Type | Key | Description |
|
|
226
|
+
| ----------------------- | ------- | ------ | ------------------------------------------------- |
|
|
227
|
+
| id | string | pk | Unique identifier for each lead |
|
|
228
|
+
| email | string | unique | Email address of the lead |
|
|
229
|
+
| emailVerified | boolean | | Whether the email is verified |
|
|
230
|
+
| verificationEmailSentAt | Date | ? | Timestamp of when the verification email was sent |
|
|
231
|
+
| metadata | json | ? | Additional data about the lead |
|
|
232
|
+
| createdAt | date | | Timestamp of lead creation |
|
|
233
|
+
| updatedAt | date | | Timestamp of last update |
|
|
202
234
|
|
|
203
235
|
#### Prisma
|
|
204
236
|
|
|
205
237
|
```prisma
|
|
206
238
|
model Lead {
|
|
207
|
-
id
|
|
208
|
-
createdAt
|
|
209
|
-
updatedAt
|
|
210
|
-
email
|
|
211
|
-
emailVerified
|
|
212
|
-
|
|
239
|
+
id String @id
|
|
240
|
+
createdAt DateTime @default(now())
|
|
241
|
+
updatedAt DateTime @updatedAt
|
|
242
|
+
email String
|
|
243
|
+
emailVerified Boolean @default(false)
|
|
244
|
+
verificationEmailSentAt DateTime?
|
|
245
|
+
metadata String?
|
|
213
246
|
|
|
214
247
|
@@unique([email])
|
|
215
248
|
@@map("lead")
|
package/dist/client.d.mts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { t as lead } from "./index-
|
|
2
|
-
import
|
|
1
|
+
import { r as LeadOptions, t as lead } from "./index-BKKjl5VY.mjs";
|
|
2
|
+
import { StandardSchemaV1 } from "better-auth";
|
|
3
3
|
|
|
4
4
|
//#region src/client.d.ts
|
|
5
|
-
declare const leadClient: () => {
|
|
5
|
+
declare const leadClient: <TMetadata = undefined>() => {
|
|
6
6
|
id: "lead";
|
|
7
|
-
$InferServerPlugin: ReturnType<typeof lead
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
INVALID_METADATA: better_auth0.RawError<"INVALID_METADATA">;
|
|
13
|
-
};
|
|
7
|
+
$InferServerPlugin: ReturnType<typeof lead<[TMetadata] extends [undefined] ? LeadOptions : LeadOptions & {
|
|
8
|
+
metadata: {
|
|
9
|
+
validationSchema: StandardSchemaV1<unknown, TMetadata>;
|
|
10
|
+
};
|
|
11
|
+
}>>;
|
|
14
12
|
};
|
|
15
13
|
//#endregion
|
|
16
14
|
export { leadClient };
|
package/dist/client.mjs
CHANGED
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport {
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { StandardSchemaV1 } from 'better-auth';\nimport type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport type { lead } from './index';\nimport type { LeadOptions } from './type';\n\nexport const leadClient = <TMetadata = undefined>() => {\n type O = [TMetadata] extends [undefined]\n ? LeadOptions\n : LeadOptions & { metadata: { validationSchema: StandardSchemaV1<unknown, TMetadata> } };\n return {\n id: 'lead',\n $InferServerPlugin: {} as ReturnType<typeof lead<O>>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";AAMA,MAAa,mBAA0C;CAIrD,OAAO;EACL,IAAI;EACJ,oBAAoB,CAAC;CACvB;AACF"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import * as better_auth0 from "better-auth";
|
|
2
1
|
import { InferOptionSchema, StandardSchemaV1 } from "better-auth";
|
|
3
|
-
import * as zod from "zod";
|
|
4
2
|
|
|
5
3
|
//#region src/schema.d.ts
|
|
6
4
|
declare const lead$1: {
|
|
@@ -55,12 +53,14 @@ interface LeadOptions {
|
|
|
55
53
|
* @param lead the lead to send the verification email to
|
|
56
54
|
* @param url the verification url
|
|
57
55
|
* @param token the verification token
|
|
56
|
+
* @param unsubscribeUrl the one-click unsubscribe URL (RFC 8058) to include in List-Unsubscribe headers
|
|
58
57
|
*/
|
|
59
58
|
|
|
60
59
|
data: {
|
|
61
60
|
lead: Lead;
|
|
62
61
|
url: string;
|
|
63
62
|
token: string;
|
|
63
|
+
unsubscribeUrl: string;
|
|
64
64
|
}, request?: Request) => Promise<boolean>;
|
|
65
65
|
onEmailVerified?: (
|
|
66
66
|
/**
|
|
@@ -156,45 +156,69 @@ declare const lead: <O extends LeadOptions>(options?: O) => {
|
|
|
156
156
|
};
|
|
157
157
|
};
|
|
158
158
|
endpoints: {
|
|
159
|
-
subscribe:
|
|
159
|
+
subscribe: import("better-auth").StrictEndpoint<"/lead/subscribe", {
|
|
160
160
|
method: "POST";
|
|
161
|
-
body: zod.ZodObject<{
|
|
162
|
-
email: zod.ZodString;
|
|
163
|
-
metadata: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
164
|
-
},
|
|
161
|
+
body: import("zod").ZodObject<{
|
|
162
|
+
email: import("zod").ZodString;
|
|
163
|
+
metadata: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
|
|
164
|
+
}, import("better-auth").$strip>;
|
|
165
|
+
metadata: {
|
|
166
|
+
$Infer: {
|
|
167
|
+
body: {
|
|
168
|
+
email: string;
|
|
169
|
+
metadata?: (O extends {
|
|
170
|
+
metadata: {
|
|
171
|
+
validationSchema: import("better-auth").StandardSchemaV1<unknown, infer Out>;
|
|
172
|
+
};
|
|
173
|
+
} ? Out : Record<string, any>) | undefined;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
};
|
|
165
177
|
}, {
|
|
166
178
|
status: boolean;
|
|
167
179
|
}>;
|
|
168
|
-
verify:
|
|
180
|
+
verify: import("better-auth").StrictEndpoint<"/lead/verify", {
|
|
169
181
|
method: "GET";
|
|
170
|
-
query: zod.ZodObject<{
|
|
171
|
-
token: zod.ZodString;
|
|
172
|
-
},
|
|
182
|
+
query: import("zod").ZodObject<{
|
|
183
|
+
token: import("zod").ZodString;
|
|
184
|
+
}, import("better-auth").$strip>;
|
|
173
185
|
}, {
|
|
174
186
|
status: boolean;
|
|
175
187
|
}>;
|
|
176
|
-
unsubscribe:
|
|
188
|
+
unsubscribe: import("better-auth").StrictEndpoint<"/lead/unsubscribe", {
|
|
177
189
|
method: "POST";
|
|
178
|
-
body: zod.ZodObject<{
|
|
179
|
-
id: zod.ZodString;
|
|
180
|
-
},
|
|
190
|
+
body: import("zod").ZodObject<{
|
|
191
|
+
id: import("zod").ZodString;
|
|
192
|
+
}, import("better-auth").$strip>;
|
|
181
193
|
}, {
|
|
182
194
|
status: boolean;
|
|
183
195
|
}>;
|
|
184
|
-
resend:
|
|
196
|
+
resend: import("better-auth").StrictEndpoint<"/lead/resend", {
|
|
185
197
|
method: "POST";
|
|
186
|
-
body: zod.ZodObject<{
|
|
187
|
-
email: zod.ZodString;
|
|
188
|
-
},
|
|
198
|
+
body: import("zod").ZodObject<{
|
|
199
|
+
email: import("zod").ZodString;
|
|
200
|
+
}, import("better-auth").$strip>;
|
|
189
201
|
}, {
|
|
190
202
|
status: boolean;
|
|
191
203
|
}>;
|
|
192
|
-
update:
|
|
204
|
+
update: import("better-auth").StrictEndpoint<"/lead/update", {
|
|
193
205
|
method: "POST";
|
|
194
|
-
body: zod.ZodObject<{
|
|
195
|
-
id: zod.ZodString;
|
|
196
|
-
metadata: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
197
|
-
},
|
|
206
|
+
body: import("zod").ZodObject<{
|
|
207
|
+
id: import("zod").ZodString;
|
|
208
|
+
metadata: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
|
|
209
|
+
}, import("better-auth").$strip>;
|
|
210
|
+
metadata: {
|
|
211
|
+
$Infer: {
|
|
212
|
+
body: {
|
|
213
|
+
id: string;
|
|
214
|
+
metadata?: (O extends {
|
|
215
|
+
metadata: {
|
|
216
|
+
validationSchema: import("better-auth").StandardSchemaV1<unknown, infer Out>;
|
|
217
|
+
};
|
|
218
|
+
} ? Out : Record<string, any>) | undefined;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
};
|
|
198
222
|
}, {
|
|
199
223
|
status: boolean;
|
|
200
224
|
}>;
|
|
@@ -206,12 +230,12 @@ declare const lead: <O extends LeadOptions>(options?: O) => {
|
|
|
206
230
|
max: number;
|
|
207
231
|
}[];
|
|
208
232
|
$ERROR_CODES: {
|
|
209
|
-
INVALID_EMAIL:
|
|
210
|
-
INVALID_TOKEN:
|
|
211
|
-
TOKEN_EXPIRED:
|
|
212
|
-
INVALID_METADATA:
|
|
233
|
+
INVALID_EMAIL: import("better-auth").RawError<"INVALID_EMAIL">;
|
|
234
|
+
INVALID_TOKEN: import("better-auth").RawError<"INVALID_TOKEN">;
|
|
235
|
+
TOKEN_EXPIRED: import("better-auth").RawError<"TOKEN_EXPIRED">;
|
|
236
|
+
INVALID_METADATA: import("better-auth").RawError<"INVALID_METADATA">;
|
|
213
237
|
};
|
|
214
238
|
};
|
|
215
239
|
//#endregion
|
|
216
240
|
export { LeadPayload as i, Lead as n, LeadOptions as r, lead as t };
|
|
217
|
-
//# sourceMappingURL=index-
|
|
241
|
+
//# sourceMappingURL=index-BKKjl5VY.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as LeadPayload, n as Lead, r as LeadOptions, t as lead } from "./index-
|
|
2
|
-
export { Lead, LeadOptions, LeadPayload, lead };
|
|
1
|
+
import { i as LeadPayload, n as Lead, r as LeadOptions, t as lead } from "./index-BKKjl5VY.mjs";
|
|
2
|
+
export { type Lead, type LeadOptions, type LeadPayload, lead };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BASE_ERROR_CODES } from "better-auth";
|
|
1
|
+
import { BASE_ERROR_CODES, defineErrorCodes } from "better-auth";
|
|
3
2
|
import { APIError, createAuthEndpoint, createEmailVerificationToken } from "better-auth/api";
|
|
4
3
|
import { jwtVerify } from "jose";
|
|
5
4
|
import { JWTExpired } from "jose/errors";
|
|
6
5
|
import * as z from "zod";
|
|
7
6
|
import { mergeSchema } from "better-auth/db";
|
|
7
|
+
//#region src/error-codes.ts
|
|
8
|
+
const LEAD_ERROR_CODES = defineErrorCodes({
|
|
9
|
+
INVALID_EMAIL: "Invalid email",
|
|
10
|
+
INVALID_TOKEN: "Invalid token",
|
|
11
|
+
TOKEN_EXPIRED: "Token expired",
|
|
12
|
+
INVALID_METADATA: "Invalid metadata"
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
8
15
|
//#region src/routes.ts
|
|
9
16
|
const subscribeSchema = z.object({
|
|
10
17
|
email: z.string().meta({ description: "Email address of the lead" }),
|
|
@@ -12,7 +19,8 @@ const subscribeSchema = z.object({
|
|
|
12
19
|
});
|
|
13
20
|
const subscribe = (options) => createAuthEndpoint("/lead/subscribe", {
|
|
14
21
|
method: "POST",
|
|
15
|
-
body: subscribeSchema
|
|
22
|
+
body: subscribeSchema,
|
|
23
|
+
metadata: { $Infer: { body: {} } }
|
|
16
24
|
}, async (ctx) => {
|
|
17
25
|
const { email } = ctx.body;
|
|
18
26
|
if (!z.email().safeParse(email).success) throw APIError.from("BAD_REQUEST", LEAD_ERROR_CODES.INVALID_EMAIL);
|
|
@@ -46,10 +54,12 @@ const subscribe = (options) => createAuthEndpoint("/lead/subscribe", {
|
|
|
46
54
|
if (options.sendVerificationEmail && lead && !lead.emailVerified) {
|
|
47
55
|
const token = await createEmailVerificationToken(ctx.context.secret, normalizedEmail, void 0, options.expiresIn ?? 3600);
|
|
48
56
|
const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;
|
|
57
|
+
const unsubscribeUrl = `${ctx.context.baseURL}/lead/unsubscribe`;
|
|
49
58
|
if (await options.sendVerificationEmail({
|
|
50
59
|
lead,
|
|
51
60
|
url,
|
|
52
|
-
token
|
|
61
|
+
token,
|
|
62
|
+
unsubscribeUrl
|
|
53
63
|
}, ctx.request)) await ctx.context.adapter.update({
|
|
54
64
|
model: "lead",
|
|
55
65
|
where: [{
|
|
@@ -137,10 +147,12 @@ const resend = (options) => createAuthEndpoint("/lead/resend", {
|
|
|
137
147
|
if (options.sendVerificationEmail && lead && !lead.emailVerified) {
|
|
138
148
|
const token = await createEmailVerificationToken(ctx.context.secret, normalizedEmail, void 0, options.expiresIn ?? 3600);
|
|
139
149
|
const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;
|
|
150
|
+
const unsubscribeUrl = `${ctx.context.baseURL}/lead/unsubscribe`;
|
|
140
151
|
if (await options.sendVerificationEmail({
|
|
141
152
|
lead,
|
|
142
153
|
url,
|
|
143
|
-
token
|
|
154
|
+
token,
|
|
155
|
+
unsubscribeUrl
|
|
144
156
|
}, ctx.request)) await ctx.context.adapter.update({
|
|
145
157
|
model: "lead",
|
|
146
158
|
where: [{
|
|
@@ -158,7 +170,8 @@ const updateSchema = z.object({
|
|
|
158
170
|
});
|
|
159
171
|
const update = (options) => createAuthEndpoint("/lead/update", {
|
|
160
172
|
method: "POST",
|
|
161
|
-
body: updateSchema
|
|
173
|
+
body: updateSchema,
|
|
174
|
+
metadata: { $Infer: { body: {} } }
|
|
162
175
|
}, async (ctx) => {
|
|
163
176
|
const { id } = ctx.body;
|
|
164
177
|
if (!await ctx.context.adapter.findOne({
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["lead"],"sources":["../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { BASE_ERROR_CODES, type InternalLogger } from 'better-auth';\nimport { APIError, createAuthEndpoint, createEmailVerificationToken } from 'better-auth/api';\nimport { jwtVerify } from 'jose';\nimport type { JWTPayload, JWTVerifyResult } from 'jose';\nimport { JWTExpired } from 'jose/errors';\nimport * as z from 'zod';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport type { Lead, LeadOptions, LeadPayload } from './type';\n\nconst subscribeSchema = z.object({\n email: z.string().meta({\n description: 'Email address of the lead',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const subscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/subscribe',\n {\n method: 'POST',\n body: subscribeSchema,\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const metadata = validateMetadata(options, ctx.body.metadata, ctx.context.logger);\n\n const normalizedEmail = email.toLowerCase();\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n try {\n lead = await ctx.context.adapter.create<LeadPayload, Lead>({\n model: 'lead',\n data: {\n email: normalizedEmail,\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n } catch (e) {\n ctx.context.logger.info('Error creating lead');\n lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n }\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n const sent = await options.sendVerificationEmail(\n {\n lead,\n url,\n token,\n },\n ctx.request,\n );\n\n if (sent) {\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [{ field: 'email', value: normalizedEmail }],\n update: { verificationEmailSentAt: new Date() },\n });\n }\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst verifySchema = z.object({\n token: z.string().meta({\n description: 'The token to verify the email',\n }),\n});\n\nexport const verify = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/verify',\n {\n method: 'GET',\n query: verifySchema,\n },\n async (ctx) => {\n const { token } = ctx.query;\n\n let jwt: JWTVerifyResult<JWTPayload>;\n try {\n jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), {\n algorithms: ['HS256'],\n });\n } catch (e) {\n if (e instanceof JWTExpired) {\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.TOKEN_EXPIRED);\n }\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.INVALID_TOKEN);\n }\n\n const parsed = subscribeSchema.parse(jwt.payload);\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (lead.emailVerified) {\n return ctx.json({\n status: true,\n });\n }\n\n lead = await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n update: {\n emailVerified: true,\n },\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.onEmailVerified) {\n await ctx.context.runInBackgroundOrAwait(options.onEmailVerified({ lead }, ctx.request));\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst unsubscribeSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to unsubscribe',\n }),\n});\n\nexport const unsubscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/unsubscribe',\n {\n method: 'POST',\n body: unsubscribeSchema,\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.delete({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst resendSchema = z.object({\n email: z.string().meta({\n description: 'Email address to resend the verification email to',\n }),\n});\n\nexport const resend = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/resend',\n {\n method: 'POST',\n body: resendSchema,\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const normalizedEmail = email.toLowerCase();\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n const sent = await options.sendVerificationEmail(\n {\n lead,\n url,\n token,\n },\n ctx.request,\n );\n\n if (sent) {\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [{ field: 'email', value: normalizedEmail }],\n update: { verificationEmailSentAt: new Date() },\n });\n }\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst updateSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to update',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const update = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/update',\n {\n method: 'POST',\n body: updateSchema,\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n const metadata = validateMetadata(options, ctx.body.metadata, ctx.context.logger);\n\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n update: {\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nfunction validateMetadata(\n options: LeadOptions,\n metadata: Record<string, any> | undefined,\n logger: InternalLogger,\n) {\n if (!metadata || !options.metadata?.validationSchema) {\n return metadata;\n }\n const validationResult = options.metadata.validationSchema['~standard'].validate(metadata);\n\n if (validationResult instanceof Promise) {\n throw APIError.from('INTERNAL_SERVER_ERROR', BASE_ERROR_CODES.ASYNC_VALIDATION_NOT_SUPPORTED);\n }\n\n if (validationResult.issues) {\n logger.error('Invalid metadata', validationResult.issues);\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_METADATA);\n }\n\n return validationResult.value as Record<string, any>;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { LeadOptions } from './type';\n\nexport const lead = {\n lead: {\n fields: {\n createdAt: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n input: false,\n },\n updatedAt: {\n type: 'date',\n defaultValue: () => new Date(),\n onUpdate: () => new Date(),\n required: true,\n input: false,\n },\n email: {\n type: 'string',\n required: true,\n unique: true,\n },\n emailVerified: {\n type: 'boolean',\n defaultValue: false,\n required: true,\n input: false,\n },\n verificationEmailSentAt: {\n type: 'date',\n required: false,\n input: false,\n },\n metadata: {\n type: 'string',\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends LeadOptions>(options: O) => {\n return mergeSchema(lead, options.schema);\n};\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport { resend, subscribe, unsubscribe, update, verify } from './routes';\nimport { getSchema } from './schema';\nimport type { LeadOptions } from './type';\n\nexport const lead = <O extends LeadOptions>(options: O = {} as O) => {\n return {\n id: 'lead',\n schema: getSchema(options),\n endpoints: {\n subscribe: subscribe(options),\n verify: verify(options),\n unsubscribe: unsubscribe(options),\n resend: resend(options),\n update: update(options),\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/lead/subscribe', '/lead/resend'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 3,\n },\n ],\n $ERROR_CODES: LEAD_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\nexport type * from './type';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["lead"],"sources":["../src/error-codes.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const LEAD_ERROR_CODES = defineErrorCodes({\n INVALID_EMAIL: 'Invalid email',\n INVALID_TOKEN: 'Invalid token',\n TOKEN_EXPIRED: 'Token expired',\n INVALID_METADATA: 'Invalid metadata',\n});\n","import { BASE_ERROR_CODES, type InternalLogger, type StandardSchemaV1 } from 'better-auth';\nimport { APIError, createAuthEndpoint, createEmailVerificationToken } from 'better-auth/api';\nimport { jwtVerify } from 'jose';\nimport type { JWTPayload, JWTVerifyResult } from 'jose';\nimport { JWTExpired } from 'jose/errors';\nimport * as z from 'zod';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport type { Lead, LeadOptions, LeadPayload } from './type';\n\ntype InferMetadata<O extends LeadOptions> = O extends {\n metadata: { validationSchema: StandardSchemaV1<unknown, infer Out> };\n}\n ? Out\n : Record<string, any>;\n\nconst subscribeSchema = z.object({\n email: z.string().meta({\n description: 'Email address of the lead',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const subscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/subscribe',\n {\n method: 'POST',\n body: subscribeSchema,\n metadata: {\n $Infer: {\n body: {} as {\n email: string;\n metadata?: InferMetadata<O>;\n },\n },\n },\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const metadata = validateMetadata(\n options,\n ctx.body.metadata as Record<string, any> | undefined,\n ctx.context.logger,\n );\n\n const normalizedEmail = email.toLowerCase();\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n try {\n lead = await ctx.context.adapter.create<LeadPayload, Lead>({\n model: 'lead',\n data: {\n email: normalizedEmail,\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n } catch (e) {\n ctx.context.logger.info('Error creating lead');\n lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n }\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n const unsubscribeUrl = `${ctx.context.baseURL}/lead/unsubscribe`;\n\n const sent = await options.sendVerificationEmail(\n {\n lead,\n url,\n token,\n unsubscribeUrl,\n },\n ctx.request,\n );\n\n if (sent) {\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [{ field: 'email', value: normalizedEmail }],\n update: { verificationEmailSentAt: new Date() },\n });\n }\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst verifySchema = z.object({\n token: z.string().meta({\n description: 'The token to verify the email',\n }),\n});\n\nexport const verify = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/verify',\n {\n method: 'GET',\n query: verifySchema,\n },\n async (ctx) => {\n const { token } = ctx.query;\n\n let jwt: JWTVerifyResult<JWTPayload>;\n try {\n jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), {\n algorithms: ['HS256'],\n });\n } catch (e) {\n if (e instanceof JWTExpired) {\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.TOKEN_EXPIRED);\n }\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.INVALID_TOKEN);\n }\n\n const parsed = subscribeSchema.parse(jwt.payload);\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (lead.emailVerified) {\n return ctx.json({\n status: true,\n });\n }\n\n lead = await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n update: {\n emailVerified: true,\n },\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.onEmailVerified) {\n await ctx.context.runInBackgroundOrAwait(options.onEmailVerified({ lead }, ctx.request));\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst unsubscribeSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to unsubscribe',\n }),\n});\n\nexport const unsubscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/unsubscribe',\n {\n method: 'POST',\n body: unsubscribeSchema,\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.delete({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst resendSchema = z.object({\n email: z.string().meta({\n description: 'Email address to resend the verification email to',\n }),\n});\n\nexport const resend = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/resend',\n {\n method: 'POST',\n body: resendSchema,\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const normalizedEmail = email.toLowerCase();\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n const unsubscribeUrl = `${ctx.context.baseURL}/lead/unsubscribe`;\n\n const sent = await options.sendVerificationEmail(\n {\n lead,\n url,\n token,\n unsubscribeUrl,\n },\n ctx.request,\n );\n\n if (sent) {\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [{ field: 'email', value: normalizedEmail }],\n update: { verificationEmailSentAt: new Date() },\n });\n }\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst updateSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to update',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const update = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/update',\n {\n method: 'POST',\n body: updateSchema,\n metadata: {\n $Infer: {\n body: {} as {\n id: string;\n metadata?: InferMetadata<O>;\n },\n },\n },\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n const metadata = validateMetadata(\n options,\n ctx.body.metadata as Record<string, any> | undefined,\n ctx.context.logger,\n );\n\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n update: {\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nfunction validateMetadata(\n options: LeadOptions,\n metadata: Record<string, any> | undefined,\n logger: InternalLogger,\n) {\n if (!metadata || !options.metadata?.validationSchema) {\n return metadata;\n }\n const validationResult = options.metadata.validationSchema['~standard'].validate(metadata);\n\n if (validationResult instanceof Promise) {\n throw APIError.from('INTERNAL_SERVER_ERROR', BASE_ERROR_CODES.ASYNC_VALIDATION_NOT_SUPPORTED);\n }\n\n if (validationResult.issues) {\n logger.error('Invalid metadata', validationResult.issues);\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_METADATA);\n }\n\n return validationResult.value as Record<string, any>;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { LeadOptions } from './type';\n\nexport const lead = {\n lead: {\n fields: {\n createdAt: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n input: false,\n },\n updatedAt: {\n type: 'date',\n defaultValue: () => new Date(),\n onUpdate: () => new Date(),\n required: true,\n input: false,\n },\n email: {\n type: 'string',\n required: true,\n unique: true,\n },\n emailVerified: {\n type: 'boolean',\n defaultValue: false,\n required: true,\n input: false,\n },\n verificationEmailSentAt: {\n type: 'date',\n required: false,\n input: false,\n },\n metadata: {\n type: 'string',\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends LeadOptions>(options: O) => {\n return mergeSchema(lead, options.schema);\n};\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport { resend, subscribe, unsubscribe, update, verify } from './routes';\nimport { getSchema } from './schema';\nimport type { LeadOptions } from './type';\n\nexport const lead = <O extends LeadOptions>(options: O = {} as O) => {\n return {\n id: 'lead',\n schema: getSchema(options),\n endpoints: {\n subscribe: subscribe(options),\n verify: verify(options),\n unsubscribe: unsubscribe(options),\n resend: resend(options),\n update: update(options),\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/lead/subscribe', '/lead/resend'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 3,\n },\n ],\n $ERROR_CODES: LEAD_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\nexport type * from './type';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth-lead",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Better Auth Lead plugin",
|
|
5
5
|
"homepage": "https://github.com/marcjulian/better-auth-plugins#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -25,15 +25,15 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"jose": "^6.2.
|
|
29
|
-
"zod": "^4.3
|
|
28
|
+
"jose": "^6.2.3",
|
|
29
|
+
"zod": "^4.4.3"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^25.3.5",
|
|
33
33
|
"bumpp": "^10.4.1",
|
|
34
|
-
"tsdown": "^0.
|
|
34
|
+
"tsdown": "^0.22.0",
|
|
35
35
|
"typescript": "^5.9.3",
|
|
36
|
-
"vitest": "^4.
|
|
36
|
+
"vitest": "^4.1.7"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"better-auth": "^1.5.0"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineErrorCodes } from "better-auth";
|
|
2
|
-
//#region src/error-codes.ts
|
|
3
|
-
const LEAD_ERROR_CODES = defineErrorCodes({
|
|
4
|
-
INVALID_EMAIL: "Invalid email",
|
|
5
|
-
INVALID_TOKEN: "Invalid token",
|
|
6
|
-
TOKEN_EXPIRED: "Token expired",
|
|
7
|
-
INVALID_METADATA: "Invalid metadata"
|
|
8
|
-
});
|
|
9
|
-
//#endregion
|
|
10
|
-
export { LEAD_ERROR_CODES as t };
|
|
11
|
-
|
|
12
|
-
//# sourceMappingURL=error-codes-CZUPEOrE.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes-CZUPEOrE.mjs","names":[],"sources":["../src/error-codes.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const LEAD_ERROR_CODES = defineErrorCodes({\n INVALID_EMAIL: 'Invalid email',\n INVALID_TOKEN: 'Invalid token',\n TOKEN_EXPIRED: 'Token expired',\n INVALID_METADATA: 'Invalid metadata',\n});\n"],"mappings":";;AAEA,MAAa,mBAAmB,iBAAiB;CAC/C,eAAe;CACf,eAAe;CACf,eAAe;CACf,kBAAkB;CACnB,CAAC"}
|