better-auth-cookie-consent 0.1.1 → 0.3.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +27 -28
- package/dist/client.d.mts +30 -30
- package/dist/client.mjs +15 -6
- package/dist/client.mjs.map +1 -1
- package/dist/{index-B8ovmV25.d.mts → index-B6ZS0bF2.d.mts} +37 -33
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +55 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -11
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marc Stammerjohann, Gary Großgarten
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ GDPR-compliant cookie consent management plugin for [Better Auth](https://better
|
|
|
7
7
|
- Automatic merge of anonymous consent on sign-in / sign-up
|
|
8
8
|
- Consent versioning with automatic invalidation
|
|
9
9
|
- Validation schema support (e.g. Zod) to enforce consent shape
|
|
10
|
-
- Generic client plugin — `cookieConsentClient<
|
|
10
|
+
- Generic client plugin — `cookieConsentClient<DefaultConsentModel>()` types your consent model end-to-end
|
|
11
11
|
|
|
12
12
|
## How It Works
|
|
13
13
|
|
|
@@ -113,7 +113,7 @@ export const auth = betterAuth({
|
|
|
113
113
|
The plugin validates consent JSON against a `StandardSchemaV1` (e.g. Zod schema) on every write. A preset `defaultConsentSchema` is exported for the standard categories:
|
|
114
114
|
|
|
115
115
|
```ts
|
|
116
|
-
import { defaultConsentSchema } from 'better-auth-cookie-consent';
|
|
116
|
+
import { defaultConsentSchema } from 'better-auth-cookie-consent/client';
|
|
117
117
|
|
|
118
118
|
// Equivalent to:
|
|
119
119
|
// z.object({
|
|
@@ -126,19 +126,14 @@ import { defaultConsentSchema } from 'better-auth-cookie-consent';
|
|
|
126
126
|
|
|
127
127
|
## Client Setup
|
|
128
128
|
|
|
129
|
-
The client plugin accepts a generic type parameter for the consent shape. Use `
|
|
129
|
+
The client plugin accepts a generic type parameter for the consent shape. Use `DefaultConsentModel` to get full end-to-end typing:
|
|
130
130
|
|
|
131
131
|
```ts
|
|
132
132
|
import { createAuthClient } from 'better-auth/client';
|
|
133
|
-
import {
|
|
134
|
-
import { cookieConsentClient } from 'better-auth-cookie-consent/client';
|
|
135
|
-
import type { z } from 'zod';
|
|
136
|
-
|
|
137
|
-
type ConsentModel = z.infer<typeof defaultConsentSchema>;
|
|
138
|
-
// { necessary: boolean; analytics: boolean; marketing: boolean; functional: boolean }
|
|
133
|
+
import { cookieConsentClient, type DefaultConsentModel } from 'better-auth-cookie-consent/client';
|
|
139
134
|
|
|
140
135
|
export const authClient = createAuthClient({
|
|
141
|
-
plugins: [cookieConsentClient<
|
|
136
|
+
plugins: [cookieConsentClient<DefaultConsentModel>()],
|
|
142
137
|
});
|
|
143
138
|
```
|
|
144
139
|
|
|
@@ -250,11 +245,9 @@ The consent object is validated against the server's `validationSchema` on every
|
|
|
250
245
|
Use `keyof` on the inferred consent model to type your banner categories:
|
|
251
246
|
|
|
252
247
|
```ts
|
|
253
|
-
import type
|
|
254
|
-
import { defaultConsentSchema } from 'better-auth-cookie-consent';
|
|
248
|
+
import { type DefaultConsentModel } from 'better-auth-cookie-consent/client';
|
|
255
249
|
|
|
256
|
-
type
|
|
257
|
-
type CategoryId = keyof ConsentModel;
|
|
250
|
+
type CategoryId = keyof DefaultConsentModel;
|
|
258
251
|
|
|
259
252
|
const CATEGORIES: { id: CategoryId; label: string }[] = [
|
|
260
253
|
{ id: 'necessary', label: 'Necessary' },
|
|
@@ -296,30 +289,36 @@ The plugin registers a server-side `after` hook that runs on every `sign-in/*` a
|
|
|
296
289
|
|
|
297
290
|
When the `consentVersion` option changes, the `getConsent` endpoint returns `versionMatch: false` so the client knows to prompt for re-consent.
|
|
298
291
|
|
|
299
|
-
##
|
|
292
|
+
## Schema
|
|
293
|
+
|
|
294
|
+
### CookieConsent
|
|
300
295
|
|
|
301
|
-
|
|
296
|
+
Table name: `cookieConsent`
|
|
302
297
|
|
|
303
|
-
|
|
|
304
|
-
| -------------- | ------- |
|
|
305
|
-
| id | string |
|
|
306
|
-
| userId | string? |
|
|
307
|
-
| anonymousId | string | Anonymous client identifier
|
|
308
|
-
| consent | string | JSON-encoded consent preferences
|
|
309
|
-
| consentVersion | string | Consent policy version
|
|
310
|
-
| timestamp | date | When consent was recorded
|
|
298
|
+
| Field | Type | Key | Description |
|
|
299
|
+
| -------------- | ------- | ------ | ----------------------------------------- |
|
|
300
|
+
| id | string | pk | Unique identifier for each consent record |
|
|
301
|
+
| userId | string? | unique | ID of an associated better-auth user |
|
|
302
|
+
| anonymousId | string | unique | Anonymous client identifier |
|
|
303
|
+
| consent | string | | JSON-encoded consent preferences |
|
|
304
|
+
| consentVersion | string | | Consent policy version |
|
|
305
|
+
| timestamp | date | | When consent was recorded |
|
|
311
306
|
|
|
312
|
-
|
|
307
|
+
#### Prisma
|
|
313
308
|
|
|
314
309
|
```prisma
|
|
315
310
|
model CookieConsent {
|
|
316
|
-
id String @id
|
|
311
|
+
id String @id
|
|
317
312
|
userId String?
|
|
313
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
318
314
|
anonymousId String
|
|
319
315
|
consent String
|
|
320
316
|
consentVersion String
|
|
321
|
-
timestamp DateTime
|
|
322
|
-
|
|
317
|
+
timestamp DateTime
|
|
318
|
+
|
|
319
|
+
@@unique([userId])
|
|
320
|
+
@@unique([anonymousId])
|
|
321
|
+
@@map("cookieConsent")
|
|
323
322
|
}
|
|
324
323
|
```
|
|
325
324
|
|
package/dist/client.d.mts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { n as cookieConsentPlugin, o as Consent } from "./index-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import * as better_auth_client0 from "better-auth/client";
|
|
5
|
-
|
|
1
|
+
import { n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema } from "./index-B6ZS0bF2.mjs";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { BetterFetch } from "better-auth/client";
|
|
6
4
|
//#region src/client.d.ts
|
|
7
5
|
/**
|
|
8
6
|
* Client-side consent state, kept in sync with the server.
|
|
@@ -12,29 +10,31 @@ interface ConsentState<TConsent extends Consent = Consent> {
|
|
|
12
10
|
consentVersion: string | null;
|
|
13
11
|
versionMatch: boolean;
|
|
14
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Inferred type from the default consent schema.
|
|
15
|
+
*/
|
|
16
|
+
type DefaultConsentModel = z.infer<typeof defaultConsentSchema>;
|
|
15
17
|
/**
|
|
16
18
|
* Client plugin for cookie consent management.
|
|
17
19
|
*
|
|
18
|
-
* @typeParam TConsent - The consent shape,
|
|
20
|
+
* @typeParam TConsent - The consent shape, defaults to `DefaultConsentModel`.
|
|
19
21
|
*
|
|
20
22
|
* @example
|
|
21
23
|
* ```ts
|
|
22
|
-
* import {
|
|
23
|
-
* import { cookieConsentClient } from 'better-auth-cookie-consent/client';
|
|
24
|
-
* import type { z } from 'zod';
|
|
24
|
+
* import { cookieConsentClient, type DefaultConsentModel } from 'better-auth-cookie-consent/client';
|
|
25
25
|
*
|
|
26
26
|
* const authClient = createAuthClient({
|
|
27
|
-
* plugins: [cookieConsentClient<
|
|
27
|
+
* plugins: [cookieConsentClient<DefaultConsentModel>()],
|
|
28
28
|
* });
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
declare const cookieConsentClient: <TConsent extends Consent =
|
|
31
|
+
declare const cookieConsentClient: <TConsent extends Consent = Record<string, boolean>>() => {
|
|
32
32
|
id: "cookie-consent";
|
|
33
33
|
$InferServerPlugin: ReturnType<typeof cookieConsentPlugin>;
|
|
34
|
-
getAtoms(
|
|
35
|
-
$consent: nanostores.PreinitializedWritableAtom<ConsentState<TConsent>> & object;
|
|
34
|
+
getAtoms(): {
|
|
35
|
+
$consent: import("nanostores").PreinitializedWritableAtom<ConsentState<TConsent>> & object;
|
|
36
36
|
};
|
|
37
|
-
getActions($fetch:
|
|
37
|
+
getActions($fetch: BetterFetch, $store: import("better-auth").ClientStore): {
|
|
38
38
|
cookieConsent: {
|
|
39
39
|
/**
|
|
40
40
|
* Set consent preferences on the server.
|
|
@@ -53,7 +53,7 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
|
|
|
53
53
|
} | {
|
|
54
54
|
data: null;
|
|
55
55
|
error: {
|
|
56
|
-
message?: string
|
|
56
|
+
message?: string;
|
|
57
57
|
status: number;
|
|
58
58
|
statusText: string;
|
|
59
59
|
};
|
|
@@ -62,13 +62,6 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
|
|
|
62
62
|
* Retrieve consent from the server.
|
|
63
63
|
*/
|
|
64
64
|
getConsent: (anonymousId?: string) => Promise<{
|
|
65
|
-
data: null;
|
|
66
|
-
error: {
|
|
67
|
-
message?: string | undefined;
|
|
68
|
-
status: number;
|
|
69
|
-
statusText: string;
|
|
70
|
-
};
|
|
71
|
-
} | {
|
|
72
65
|
data: {
|
|
73
66
|
consent: {
|
|
74
67
|
id: string;
|
|
@@ -81,27 +74,34 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
|
|
|
81
74
|
versionMatch: boolean;
|
|
82
75
|
};
|
|
83
76
|
error: null;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Merge anonymous consent into the authenticated user's record.
|
|
87
|
-
*/
|
|
88
|
-
mergeConsent: (anonymousId: string) => Promise<{
|
|
77
|
+
} | {
|
|
89
78
|
data: null;
|
|
90
79
|
error: {
|
|
91
|
-
message?: string
|
|
80
|
+
message?: string;
|
|
92
81
|
status: number;
|
|
93
82
|
statusText: string;
|
|
94
83
|
};
|
|
95
|
-
}
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Merge anonymous consent into the authenticated user's record.
|
|
87
|
+
*/
|
|
88
|
+
mergeConsent: (anonymousId: string) => Promise<{
|
|
96
89
|
data: {
|
|
97
90
|
status: boolean;
|
|
98
91
|
merged: boolean;
|
|
99
92
|
};
|
|
100
93
|
error: null;
|
|
94
|
+
} | {
|
|
95
|
+
data: null;
|
|
96
|
+
error: {
|
|
97
|
+
message?: string;
|
|
98
|
+
status: number;
|
|
99
|
+
statusText: string;
|
|
100
|
+
};
|
|
101
101
|
}>;
|
|
102
102
|
};
|
|
103
103
|
};
|
|
104
104
|
};
|
|
105
105
|
//#endregion
|
|
106
|
-
export { ConsentState, cookieConsentClient };
|
|
106
|
+
export { ConsentState, DefaultConsentModel, cookieConsentClient };
|
|
107
107
|
//# sourceMappingURL=client.d.mts.map
|
package/dist/client.mjs
CHANGED
|
@@ -3,16 +3,14 @@ import { atom } from "nanostores";
|
|
|
3
3
|
/**
|
|
4
4
|
* Client plugin for cookie consent management.
|
|
5
5
|
*
|
|
6
|
-
* @typeParam TConsent - The consent shape,
|
|
6
|
+
* @typeParam TConsent - The consent shape, defaults to `DefaultConsentModel`.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import {
|
|
11
|
-
* import { cookieConsentClient } from 'better-auth-cookie-consent/client';
|
|
12
|
-
* import type { z } from 'zod';
|
|
10
|
+
* import { cookieConsentClient, type DefaultConsentModel } from 'better-auth-cookie-consent/client';
|
|
13
11
|
*
|
|
14
12
|
* const authClient = createAuthClient({
|
|
15
|
-
* plugins: [cookieConsentClient<
|
|
13
|
+
* plugins: [cookieConsentClient<DefaultConsentModel>()],
|
|
16
14
|
* });
|
|
17
15
|
* ```
|
|
18
16
|
*/
|
|
@@ -20,7 +18,7 @@ const cookieConsentClient = () => {
|
|
|
20
18
|
return {
|
|
21
19
|
id: "cookie-consent",
|
|
22
20
|
$InferServerPlugin: {},
|
|
23
|
-
getAtoms(
|
|
21
|
+
getAtoms() {
|
|
24
22
|
return { $consent: atom({
|
|
25
23
|
consent: null,
|
|
26
24
|
consentVersion: null,
|
|
@@ -44,6 +42,11 @@ const cookieConsentClient = () => {
|
|
|
44
42
|
return res;
|
|
45
43
|
}
|
|
46
44
|
return { cookieConsent: {
|
|
45
|
+
/**
|
|
46
|
+
* Set consent preferences on the server.
|
|
47
|
+
* Also used for accept-all / reject-all by passing the
|
|
48
|
+
* appropriate consent object (all `true` or all `false`).
|
|
49
|
+
*/
|
|
47
50
|
setConsent: async (data) => {
|
|
48
51
|
const res = await $fetch("/cookie-consent/set", {
|
|
49
52
|
method: "POST",
|
|
@@ -56,9 +59,15 @@ const cookieConsentClient = () => {
|
|
|
56
59
|
});
|
|
57
60
|
return res;
|
|
58
61
|
},
|
|
62
|
+
/**
|
|
63
|
+
* Retrieve consent from the server.
|
|
64
|
+
*/
|
|
59
65
|
getConsent: async (anonymousId) => {
|
|
60
66
|
return syncFromServer(anonymousId);
|
|
61
67
|
},
|
|
68
|
+
/**
|
|
69
|
+
* Merge anonymous consent into the authenticated user's record.
|
|
70
|
+
*/
|
|
62
71
|
mergeConsent: async (anonymousId) => {
|
|
63
72
|
const res = await $fetch("/cookie-consent/merge", {
|
|
64
73
|
method: "POST",
|
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';\nimport { atom } from 'nanostores';\
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin, BetterFetch } from 'better-auth/client';\nimport { atom } from 'nanostores';\nimport type z from 'zod';\n\nimport type { Consent, cookieConsentPlugin, defaultConsentSchema } from './index';\n\n/**\n * Client-side consent state, kept in sync with the server.\n */\nexport interface ConsentState<TConsent extends Consent = Consent> {\n consent: TConsent | null;\n consentVersion: string | null;\n versionMatch: boolean;\n}\n\n/**\n * Inferred type from the default consent schema.\n */\nexport type DefaultConsentModel = z.infer<typeof defaultConsentSchema>;\n\n/**\n * Client plugin for cookie consent management.\n *\n * @typeParam TConsent - The consent shape, defaults to `DefaultConsentModel`.\n *\n * @example\n * ```ts\n * import { cookieConsentClient, type DefaultConsentModel } from 'better-auth-cookie-consent/client';\n *\n * const authClient = createAuthClient({\n * plugins: [cookieConsentClient<DefaultConsentModel>()],\n * });\n * ```\n */\nexport const cookieConsentClient = <TConsent extends Consent = Consent>() => {\n return {\n id: 'cookie-consent',\n $InferServerPlugin: {} as ReturnType<typeof cookieConsentPlugin>,\n\n getAtoms() {\n const $consent = atom<ConsentState<TConsent>>({\n consent: null,\n consentVersion: null,\n versionMatch: false,\n });\n return { $consent };\n },\n\n getActions($fetch: BetterFetch, $store) {\n const consentAtom = $store.atoms.$consent as ReturnType<typeof atom<ConsentState<TConsent>>>;\n\n async function syncFromServer(anonymousId?: string) {\n let path = '/cookie-consent/get';\n if (anonymousId) {\n const params = new URLSearchParams({ anonymousId });\n path = `${path}?${params.toString()}`;\n }\n const res = await $fetch<{\n consent: {\n id: string;\n userId?: string | null;\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n timestamp: string;\n } | null;\n versionMatch: boolean;\n }>(`${path}`, { method: 'GET' });\n if (res.data) {\n consentAtom.set({\n consent: res.data.consent?.consent ?? null,\n consentVersion: res.data.consent?.consentVersion ?? null,\n versionMatch: res.data.versionMatch,\n });\n }\n return res;\n }\n\n return {\n cookieConsent: {\n /**\n * Set consent preferences on the server.\n * Also used for accept-all / reject-all by passing the\n * appropriate consent object (all `true` or all `false`).\n */\n setConsent: async (data: {\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n }) => {\n const res = await $fetch<{ status: boolean }>('/cookie-consent/set', {\n method: 'POST',\n body: data,\n });\n if (res.data?.status) {\n consentAtom.set({\n consent: data.consent,\n consentVersion: data.consentVersion,\n versionMatch: true,\n });\n }\n return res;\n },\n\n /**\n * Retrieve consent from the server.\n */\n getConsent: async (anonymousId?: string) => {\n return syncFromServer(anonymousId);\n },\n\n /**\n * Merge anonymous consent into the authenticated user's record.\n */\n mergeConsent: async (anonymousId: string) => {\n const res = await $fetch<{ status: boolean; merged: boolean }>(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: { anonymousId },\n },\n );\n if (res.data?.merged) {\n await syncFromServer(anonymousId);\n }\n return res;\n },\n },\n };\n },\n\n // $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAkCA,MAAa,4BAAgE;CAC3E,OAAO;EACL,IAAI;EACJ,oBAAoB,CAAC;EAErB,WAAW;GAMT,OAAO,EAAE,UALQ,KAA6B;IAC5C,SAAS;IACT,gBAAgB;IAChB,cAAc;GAChB,CACgB,EAAE;EACpB;EAEA,WAAW,QAAqB,QAAQ;GACtC,MAAM,cAAc,OAAO,MAAM;GAEjC,eAAe,eAAe,aAAsB;IAClD,IAAI,OAAO;IACX,IAAI,aAAa;KACf,MAAM,SAAS,IAAI,gBAAgB,EAAE,YAAY,CAAC;KAClD,OAAO,GAAG,KAAK,GAAG,OAAO,SAAS;IACpC;IACA,MAAM,MAAM,MAAM,OAUf,GAAG,QAAQ,EAAE,QAAQ,MAAM,CAAC;IAC/B,IAAI,IAAI,MACN,YAAY,IAAI;KACd,SAAS,IAAI,KAAK,SAAS,WAAW;KACtC,gBAAgB,IAAI,KAAK,SAAS,kBAAkB;KACpD,cAAc,IAAI,KAAK;IACzB,CAAC;IAEH,OAAO;GACT;GAEA,OAAO,EACL,eAAe;;;;;;IAMb,YAAY,OAAO,SAIb;KACJ,MAAM,MAAM,MAAM,OAA4B,uBAAuB;MACnE,QAAQ;MACR,MAAM;KACR,CAAC;KACD,IAAI,IAAI,MAAM,QACZ,YAAY,IAAI;MACd,SAAS,KAAK;MACd,gBAAgB,KAAK;MACrB,cAAc;KAChB,CAAC;KAEH,OAAO;IACT;;;;IAKA,YAAY,OAAO,gBAAyB;KAC1C,OAAO,eAAe,WAAW;IACnC;;;;IAKA,cAAc,OAAO,gBAAwB;KAC3C,MAAM,MAAM,MAAM,OAChB,yBACA;MACE,QAAQ;MACR,MAAM,EAAE,YAAY;KACtB,CACF;KACA,IAAI,IAAI,MAAM,QACZ,MAAM,eAAe,WAAW;KAElC,OAAO;IACT;GACF,EACF;EACF;CAGF;AACF"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import * as z from "zod";
|
|
2
|
-
import * as better_auth0 from "better-auth";
|
|
1
|
+
import * as z$1 from "zod";
|
|
3
2
|
import { BetterAuthOptions, DBAdapter, InferOptionSchema, StandardSchemaV1 } from "better-auth";
|
|
4
|
-
|
|
5
3
|
//#region src/schema.d.ts
|
|
6
4
|
declare const cookieConsent: {
|
|
7
5
|
cookieConsent: {
|
|
@@ -9,6 +7,7 @@ declare const cookieConsent: {
|
|
|
9
7
|
userId: {
|
|
10
8
|
type: "string";
|
|
11
9
|
required: false;
|
|
10
|
+
unique: true;
|
|
12
11
|
references: {
|
|
13
12
|
model: string;
|
|
14
13
|
field: string;
|
|
@@ -17,6 +16,7 @@ declare const cookieConsent: {
|
|
|
17
16
|
anonymousId: {
|
|
18
17
|
type: "string";
|
|
19
18
|
required: true;
|
|
19
|
+
unique: true;
|
|
20
20
|
};
|
|
21
21
|
consent: {
|
|
22
22
|
type: "string";
|
|
@@ -30,6 +30,7 @@ declare const cookieConsent: {
|
|
|
30
30
|
type: "date";
|
|
31
31
|
defaultValue: () => Date;
|
|
32
32
|
required: true;
|
|
33
|
+
input: false;
|
|
33
34
|
};
|
|
34
35
|
};
|
|
35
36
|
};
|
|
@@ -40,8 +41,8 @@ declare const cookieConsent: {
|
|
|
40
41
|
* Consent categories mapped to boolean values.
|
|
41
42
|
* Each key represents a cookie category (e.g., "analytics", "marketing").
|
|
42
43
|
*/
|
|
43
|
-
type ConsentSchemaModel = z.ZodObject<Record<string, z.ZodBoolean>>;
|
|
44
|
-
type Consent = z.infer<ConsentSchemaModel>;
|
|
44
|
+
type ConsentSchemaModel = z$1.ZodObject<Record<string, z$1.ZodBoolean>>;
|
|
45
|
+
type Consent = z$1.infer<ConsentSchemaModel>;
|
|
45
46
|
/**
|
|
46
47
|
* Options for the cookie consent plugin.
|
|
47
48
|
*/
|
|
@@ -145,12 +146,12 @@ declare const ANONYMOUS_ID_COOKIE = "cookie-consent-anon-id";
|
|
|
145
146
|
* })
|
|
146
147
|
* ```
|
|
147
148
|
*/
|
|
148
|
-
declare const defaultConsentSchema: z.ZodObject<{
|
|
149
|
-
necessary: z.ZodBoolean;
|
|
150
|
-
analytics: z.ZodBoolean;
|
|
151
|
-
marketing: z.ZodBoolean;
|
|
152
|
-
functional: z.ZodBoolean;
|
|
153
|
-
}, z.core.$strip>;
|
|
149
|
+
declare const defaultConsentSchema: z$1.ZodObject<{
|
|
150
|
+
necessary: z$1.ZodBoolean;
|
|
151
|
+
analytics: z$1.ZodBoolean;
|
|
152
|
+
marketing: z$1.ZodBoolean;
|
|
153
|
+
functional: z$1.ZodBoolean;
|
|
154
|
+
}, z$1.core.$strip>;
|
|
154
155
|
declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O) => {
|
|
155
156
|
id: "cookie-consent";
|
|
156
157
|
schema: {
|
|
@@ -159,6 +160,7 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
159
160
|
userId: {
|
|
160
161
|
type: "string";
|
|
161
162
|
required: false;
|
|
163
|
+
unique: true;
|
|
162
164
|
references: {
|
|
163
165
|
model: string;
|
|
164
166
|
field: string;
|
|
@@ -167,6 +169,7 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
167
169
|
anonymousId: {
|
|
168
170
|
type: "string";
|
|
169
171
|
required: true;
|
|
172
|
+
unique: true;
|
|
170
173
|
};
|
|
171
174
|
consent: {
|
|
172
175
|
type: "string";
|
|
@@ -180,26 +183,27 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
180
183
|
type: "date";
|
|
181
184
|
defaultValue: () => Date;
|
|
182
185
|
required: true;
|
|
186
|
+
input: false;
|
|
183
187
|
};
|
|
184
188
|
};
|
|
185
189
|
};
|
|
186
190
|
};
|
|
187
191
|
endpoints: {
|
|
188
|
-
setConsent:
|
|
192
|
+
setConsent: import("better-auth").StrictEndpoint<"/cookie-consent/set", {
|
|
189
193
|
method: "POST";
|
|
190
|
-
body: z.ZodObject<{
|
|
191
|
-
anonymousId: z.ZodString;
|
|
192
|
-
consent: z.ZodRecord<z.ZodString, z.ZodBoolean>;
|
|
193
|
-
consentVersion: z.ZodString;
|
|
194
|
-
}, z.core.$strip>;
|
|
194
|
+
body: z$1.ZodObject<{
|
|
195
|
+
anonymousId: z$1.ZodString;
|
|
196
|
+
consent: z$1.ZodRecord<z$1.ZodString, z$1.ZodBoolean>;
|
|
197
|
+
consentVersion: z$1.ZodString;
|
|
198
|
+
}, z$1.core.$strip>;
|
|
195
199
|
}, {
|
|
196
200
|
status: boolean;
|
|
197
201
|
}>;
|
|
198
|
-
getConsent:
|
|
202
|
+
getConsent: import("better-auth").StrictEndpoint<"/cookie-consent/get", {
|
|
199
203
|
method: "GET";
|
|
200
|
-
query: z.ZodObject<{
|
|
201
|
-
anonymousId: z.ZodOptional<z.ZodString>;
|
|
202
|
-
}, z.core.$strip>;
|
|
204
|
+
query: z$1.ZodObject<{
|
|
205
|
+
anonymousId: z$1.ZodOptional<z$1.ZodString>;
|
|
206
|
+
}, z$1.core.$strip>;
|
|
203
207
|
}, {
|
|
204
208
|
consent: null;
|
|
205
209
|
versionMatch: boolean;
|
|
@@ -214,11 +218,11 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
214
218
|
};
|
|
215
219
|
versionMatch: boolean;
|
|
216
220
|
}>;
|
|
217
|
-
mergeConsent:
|
|
221
|
+
mergeConsent: import("better-auth").StrictEndpoint<"/cookie-consent/merge", {
|
|
218
222
|
method: "POST";
|
|
219
|
-
body: z.ZodObject<{
|
|
220
|
-
anonymousId: z.ZodString;
|
|
221
|
-
}, z.core.$strip>;
|
|
223
|
+
body: z$1.ZodObject<{
|
|
224
|
+
anonymousId: z$1.ZodString;
|
|
225
|
+
}, z$1.core.$strip>;
|
|
222
226
|
}, {
|
|
223
227
|
status: boolean;
|
|
224
228
|
merged: boolean;
|
|
@@ -226,8 +230,8 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
226
230
|
};
|
|
227
231
|
hooks: {
|
|
228
232
|
after: {
|
|
229
|
-
matcher: (context:
|
|
230
|
-
handler: (inputContext:
|
|
233
|
+
matcher: (context: import("better-auth").HookEndpointContext) => boolean;
|
|
234
|
+
handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
|
|
231
235
|
}[];
|
|
232
236
|
};
|
|
233
237
|
options: NoInfer<O>;
|
|
@@ -237,11 +241,11 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
|
|
|
237
241
|
max: number;
|
|
238
242
|
}[];
|
|
239
243
|
$ERROR_CODES: {
|
|
240
|
-
|
|
241
|
-
CONSENT_NOT_FOUND:
|
|
242
|
-
INVALID_CONSENT:
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
AUTHENTICATION_REQUIRED: import("better-auth").RawError<"AUTHENTICATION_REQUIRED">;
|
|
245
|
+
CONSENT_NOT_FOUND: import("better-auth").RawError<"CONSENT_NOT_FOUND">;
|
|
246
|
+
INVALID_CONSENT: import("better-auth").RawError<"INVALID_CONSENT">;
|
|
247
|
+
MISSING_ANONYMOUS_ID: import("better-auth").RawError<"MISSING_ANONYMOUS_ID">;
|
|
248
|
+
VERSION_MISMATCH: import("better-auth").RawError<"VERSION_MISMATCH">;
|
|
245
249
|
};
|
|
246
250
|
};
|
|
247
251
|
/**
|
|
@@ -267,4 +271,4 @@ declare function getConsentFromCtx(ctx: {
|
|
|
267
271
|
declare function hasConsent(ctx: Parameters<typeof getConsentFromCtx>[0], category: string): Promise<boolean>;
|
|
268
272
|
//#endregion
|
|
269
273
|
export { hasConsent as a, CookieConsentOptions as c, getConsentFromCtx as i, CookieConsentPayload as l, cookieConsentPlugin as n, Consent as o, defaultConsentSchema as r, ConsentSchemaModel as s, ANONYMOUS_ID_COOKIE as t, CookieConsentRecord as u };
|
|
270
|
-
//# sourceMappingURL=index-
|
|
274
|
+
//# sourceMappingURL=index-B6ZS0bF2.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as hasConsent, c as CookieConsentOptions, i as getConsentFromCtx, l as CookieConsentPayload, n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema, s as ConsentSchemaModel, t as ANONYMOUS_ID_COOKIE, u as CookieConsentRecord } from "./index-
|
|
2
|
-
export { ANONYMOUS_ID_COOKIE, Consent, ConsentSchemaModel, CookieConsentOptions, CookieConsentPayload, CookieConsentRecord, cookieConsentPlugin, defaultConsentSchema, getConsentFromCtx, hasConsent };
|
|
1
|
+
import { a as hasConsent, c as CookieConsentOptions, i as getConsentFromCtx, l as CookieConsentPayload, n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema, s as ConsentSchemaModel, t as ANONYMOUS_ID_COOKIE, u as CookieConsentRecord } from "./index-B6ZS0bF2.mjs";
|
|
2
|
+
export { ANONYMOUS_ID_COOKIE, type Consent, type ConsentSchemaModel, type CookieConsentOptions, type CookieConsentPayload, type CookieConsentRecord, cookieConsentPlugin, defaultConsentSchema, getConsentFromCtx, hasConsent };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APIError, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx } from "better-auth/api";
|
|
2
|
-
import * as z from "zod";
|
|
2
|
+
import * as z$1 from "zod";
|
|
3
3
|
import { BASE_ERROR_CODES, defineErrorCodes } from "better-auth";
|
|
4
4
|
import { mergeSchema } from "better-auth/db";
|
|
5
5
|
//#region src/error-codes.ts
|
|
@@ -12,13 +12,13 @@ const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({
|
|
|
12
12
|
});
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/routes.ts
|
|
15
|
-
const setConsentSchema = z.object({
|
|
16
|
-
anonymousId: z.string().meta({ description: "Anonymous identifier for unauthenticated users" }),
|
|
17
|
-
consent: z.record(z.string(), z.boolean()).meta({ description: "Consent preferences as category-boolean pairs" }),
|
|
18
|
-
consentVersion: z.string().meta({ description: "Version of the consent policy" })
|
|
15
|
+
const setConsentSchema = z$1.object({
|
|
16
|
+
anonymousId: z$1.string().meta({ description: "Anonymous identifier for unauthenticated users" }),
|
|
17
|
+
consent: z$1.record(z$1.string(), z$1.boolean()).meta({ description: "Consent preferences as category-boolean pairs" }),
|
|
18
|
+
consentVersion: z$1.string().meta({ description: "Version of the consent policy" })
|
|
19
19
|
});
|
|
20
|
-
const getConsentSchema = z.object({ anonymousId: z.string().optional().meta({ description: "Anonymous identifier fallback when no session exists" }) });
|
|
21
|
-
const mergeConsentSchema = z.object({ anonymousId: z.string().meta({ description: "Anonymous identifier to merge consent from" }) });
|
|
20
|
+
const getConsentSchema = z$1.object({ anonymousId: z$1.string().optional().meta({ description: "Anonymous identifier fallback when no session exists" }) });
|
|
21
|
+
const mergeConsentSchema = z$1.object({ anonymousId: z$1.string().meta({ description: "Anonymous identifier to merge consent from" }) });
|
|
22
22
|
const setConsent = (options) => createAuthEndpoint("/cookie-consent/set", {
|
|
23
23
|
method: "POST",
|
|
24
24
|
body: setConsentSchema
|
|
@@ -112,51 +112,53 @@ const mergeConsent = (_options) => createAuthEndpoint("/cookie-consent/merge", {
|
|
|
112
112
|
* @returns `true` if consent was merged, `false` if no anonymous record was found.
|
|
113
113
|
*/
|
|
114
114
|
async function mergeAnonymousConsentToUser(adapter, userId, anonymousId) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
where: [{
|
|
118
|
-
field: "anonymousId",
|
|
119
|
-
value: anonymousId
|
|
120
|
-
}]
|
|
121
|
-
});
|
|
122
|
-
if (!anonymousRecord || anonymousRecord.userId) return false;
|
|
123
|
-
const userRecord = await adapter.findOne({
|
|
124
|
-
model: "cookieConsent",
|
|
125
|
-
where: [{
|
|
126
|
-
field: "userId",
|
|
127
|
-
value: userId
|
|
128
|
-
}]
|
|
129
|
-
});
|
|
130
|
-
if (userRecord) {
|
|
131
|
-
await adapter.update({
|
|
115
|
+
return await adapter.transaction(async (tx) => {
|
|
116
|
+
const anonymousRecord = await tx.findOne({
|
|
132
117
|
model: "cookieConsent",
|
|
133
118
|
where: [{
|
|
134
|
-
field: "
|
|
135
|
-
value:
|
|
136
|
-
}]
|
|
137
|
-
update: {
|
|
138
|
-
consent: anonymousRecord.consent,
|
|
139
|
-
consentVersion: anonymousRecord.consentVersion,
|
|
140
|
-
anonymousId,
|
|
141
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
142
|
-
}
|
|
119
|
+
field: "anonymousId",
|
|
120
|
+
value: anonymousId
|
|
121
|
+
}]
|
|
143
122
|
});
|
|
144
|
-
|
|
123
|
+
if (!anonymousRecord || anonymousRecord.userId) return false;
|
|
124
|
+
const userRecord = await tx.findOne({
|
|
125
|
+
model: "cookieConsent",
|
|
126
|
+
where: [{
|
|
127
|
+
field: "userId",
|
|
128
|
+
value: userId
|
|
129
|
+
}]
|
|
130
|
+
});
|
|
131
|
+
if (userRecord) {
|
|
132
|
+
await tx.delete({
|
|
133
|
+
model: "cookieConsent",
|
|
134
|
+
where: [{
|
|
135
|
+
field: "id",
|
|
136
|
+
value: anonymousRecord.id
|
|
137
|
+
}]
|
|
138
|
+
});
|
|
139
|
+
await tx.update({
|
|
140
|
+
model: "cookieConsent",
|
|
141
|
+
where: [{
|
|
142
|
+
field: "id",
|
|
143
|
+
value: userRecord.id
|
|
144
|
+
}],
|
|
145
|
+
update: {
|
|
146
|
+
consent: anonymousRecord.consent,
|
|
147
|
+
consentVersion: anonymousRecord.consentVersion,
|
|
148
|
+
anonymousId: anonymousRecord.anonymousId,
|
|
149
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
} else await tx.update({
|
|
145
153
|
model: "cookieConsent",
|
|
146
154
|
where: [{
|
|
147
155
|
field: "id",
|
|
148
156
|
value: anonymousRecord.id
|
|
149
|
-
}]
|
|
157
|
+
}],
|
|
158
|
+
update: { userId }
|
|
150
159
|
});
|
|
151
|
-
|
|
152
|
-
model: "cookieConsent",
|
|
153
|
-
where: [{
|
|
154
|
-
field: "id",
|
|
155
|
-
value: anonymousRecord.id
|
|
156
|
-
}],
|
|
157
|
-
update: { userId }
|
|
160
|
+
return true;
|
|
158
161
|
});
|
|
159
|
-
return true;
|
|
160
162
|
}
|
|
161
163
|
/**
|
|
162
164
|
* Find a consent record by userId (preferred) or anonymousId fallback.
|
|
@@ -201,6 +203,7 @@ const cookieConsent = { cookieConsent: { fields: {
|
|
|
201
203
|
userId: {
|
|
202
204
|
type: "string",
|
|
203
205
|
required: false,
|
|
206
|
+
unique: true,
|
|
204
207
|
references: {
|
|
205
208
|
model: "user",
|
|
206
209
|
field: "id"
|
|
@@ -208,7 +211,8 @@ const cookieConsent = { cookieConsent: { fields: {
|
|
|
208
211
|
},
|
|
209
212
|
anonymousId: {
|
|
210
213
|
type: "string",
|
|
211
|
-
required: true
|
|
214
|
+
required: true,
|
|
215
|
+
unique: true
|
|
212
216
|
},
|
|
213
217
|
consent: {
|
|
214
218
|
type: "string",
|
|
@@ -221,7 +225,8 @@ const cookieConsent = { cookieConsent: { fields: {
|
|
|
221
225
|
timestamp: {
|
|
222
226
|
type: "date",
|
|
223
227
|
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
224
|
-
required: true
|
|
228
|
+
required: true,
|
|
229
|
+
input: false
|
|
225
230
|
}
|
|
226
231
|
} } };
|
|
227
232
|
const getSchema = (options) => {
|
|
@@ -265,11 +270,11 @@ function parseCookieValue(cookieHeader, name) {
|
|
|
265
270
|
* })
|
|
266
271
|
* ```
|
|
267
272
|
*/
|
|
268
|
-
const defaultConsentSchema = z.object({
|
|
269
|
-
necessary: z.boolean(),
|
|
270
|
-
analytics: z.boolean(),
|
|
271
|
-
marketing: z.boolean(),
|
|
272
|
-
functional: z.boolean()
|
|
273
|
+
const defaultConsentSchema = z$1.object({
|
|
274
|
+
necessary: z$1.boolean(),
|
|
275
|
+
analytics: z$1.boolean(),
|
|
276
|
+
marketing: z$1.boolean(),
|
|
277
|
+
functional: z$1.boolean()
|
|
273
278
|
});
|
|
274
279
|
const cookieConsentPlugin = (options = {}) => {
|
|
275
280
|
return {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/error-codes.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({\n MISSING_ANONYMOUS_ID: 'Anonymous ID is required',\n CONSENT_NOT_FOUND: 'Cookie consent record not found',\n INVALID_CONSENT: 'Consent must be a non-empty object with boolean values',\n VERSION_MISMATCH: 'Consent version is outdated and must be renewed',\n AUTHENTICATION_REQUIRED: 'Authentication is required for this operation',\n});\n","import {\n BASE_ERROR_CODES,\n type BetterAuthOptions,\n type DBAdapter,\n type InternalLogger,\n} from 'better-auth';\nimport { APIError, createAuthEndpoint, getSessionFromCtx } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type {\n Consent,\n CookieConsentOptions,\n CookieConsentPayload,\n CookieConsentRecord,\n} from './type';\n\nconst setConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier for unauthenticated users',\n }),\n consent: z.record(z.string(), z.boolean()).meta({\n description: 'Consent preferences as category-boolean pairs',\n }),\n consentVersion: z.string().meta({\n description: 'Version of the consent policy',\n }),\n});\n\nconst getConsentSchema = z.object({\n anonymousId: z.string().optional().meta({\n description: 'Anonymous identifier fallback when no session exists',\n }),\n});\n\nconst mergeConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier to merge consent from',\n }),\n});\n\nexport const setConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/set',\n {\n method: 'POST',\n body: setConsentSchema,\n },\n async (ctx) => {\n const { anonymousId, consent, consentVersion } = ctx.body;\n\n if (!anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n if (!consent || Object.keys(consent).length === 0) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n const validatedConsent = validateConsent(options, consent, ctx.context.logger);\n\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n\n // Look for existing record by userId or anonymousId\n const existing = await findConsentRecord(ctx, userId, anonymousId);\n\n const consentJson = JSON.stringify(validatedConsent);\n const now = new Date();\n\n if (existing) {\n await ctx.context.adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: existing.id }],\n update: {\n userId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n } else {\n await ctx.context.adapter.create<CookieConsentPayload, CookieConsentRecord>({\n model: 'cookieConsent',\n data: {\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n }\n\n if (options.onConsentChange) {\n const record =\n (await findConsentRecord(ctx, userId, anonymousId)) ??\n ({\n id: '',\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n } as CookieConsentRecord);\n\n await ctx.context.runInBackgroundOrAwait(\n options.onConsentChange({ consent: record }, ctx.request),\n );\n }\n\n return ctx.json({ status: true });\n },\n );\n\nexport const getConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/get',\n {\n method: 'GET',\n query: getConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (!userId && !anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n const record = await findConsentRecord(ctx, userId, anonymousId);\n\n if (!record) {\n return ctx.json({ consent: null, versionMatch: false });\n }\n\n const currentVersion = options.consentVersion ?? 'v1';\n const versionMatch = record.consentVersion === currentVersion;\n\n return ctx.json({\n consent: {\n id: record.id,\n userId: record.userId,\n anonymousId: record.anonymousId,\n consent: JSON.parse(record.consent) as Consent,\n consentVersion: record.consentVersion,\n timestamp: record.timestamp,\n },\n versionMatch,\n });\n },\n );\n\nexport const mergeConsent = <O extends CookieConsentOptions>(_options: O) =>\n createAuthEndpoint(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: mergeConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id;\n\n if (!userId) {\n throw APIError.from('UNAUTHORIZED', COOKIE_CONSENT_ERROR_CODES.AUTHENTICATION_REQUIRED);\n }\n\n const merged = await mergeAnonymousConsentToUser(\n ctx.context.adapter,\n userId,\n ctx.body.anonymousId,\n );\n return ctx.json({ status: true, merged });\n },\n );\n\n/**\n * Merge anonymous consent into a user's record.\n * Shared between the merge endpoint and the sign-in/sign-up hook.\n *\n * @returns `true` if consent was merged, `false` if no anonymous record was found.\n */\nexport async function mergeAnonymousConsentToUser(\n adapter: DBAdapter<BetterAuthOptions>,\n userId: string,\n anonymousId: string,\n): Promise<boolean> {\n const anonymousRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n\n if (!anonymousRecord || anonymousRecord.userId) return false;\n // ^ Skip if record doesn't exist or is already linked to a user (already merged)\n\n const userRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n\n if (userRecord) {\n // User already has consent; update with anonymous data\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: userRecord.id }],\n update: {\n consent: anonymousRecord.consent,\n consentVersion: anonymousRecord.consentVersion,\n anonymousId,\n timestamp: new Date(),\n },\n });\n await adapter.delete({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n });\n } else {\n // Attach anonymous consent to user\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n update: { userId },\n });\n }\n\n return true;\n}\n\n/**\n * Find a consent record by userId (preferred) or anonymousId fallback.\n */\nasync function findConsentRecord(\n ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n };\n },\n userId: string | null | undefined,\n anonymousId: string | undefined,\n): Promise<CookieConsentRecord | null> {\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Validate the consent object against the configured validation schema.\n * Returns the validated consent or the original consent when no schema is set.\n */\nfunction validateConsent(\n options: CookieConsentOptions,\n consent: Consent,\n logger: InternalLogger,\n): Consent {\n if (!options.consent?.validationSchema) {\n return consent;\n }\n const validationResult = options.consent.validationSchema['~standard'].validate(consent);\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 consent', validationResult.issues);\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n return validationResult.value as Consent;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { CookieConsentOptions } from './type';\n\nexport const cookieConsent = {\n cookieConsent: {\n fields: {\n userId: {\n type: 'string',\n required: false,\n references: {\n model: 'user',\n field: 'id',\n },\n },\n anonymousId: {\n type: 'string',\n required: true,\n },\n consent: {\n type: 'string',\n required: true,\n },\n consentVersion: {\n type: 'string',\n required: true,\n },\n timestamp: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends CookieConsentOptions>(options: O) => {\n return mergeSchema(cookieConsent, options.schema);\n};\n","import type { BetterAuthOptions, BetterAuthPlugin, DBAdapter } from 'better-auth';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport { getConsent, mergeAnonymousConsentToUser, mergeConsent, setConsent } from './routes';\nimport { getSchema } from './schema';\nimport type {\n Consent,\n ConsentSchemaModel,\n CookieConsentOptions,\n CookieConsentRecord,\n} from './type';\n\n/**\n * Cookie name used to store the anonymous consent ID.\n * The client sets this cookie so the server-side hook can\n * read it during sign-in to auto-merge anonymous consent.\n */\nexport const ANONYMOUS_ID_COOKIE = 'cookie-consent-anon-id';\n\n/**\n * Parse a single cookie value from a raw `Cookie` header string.\n */\nfunction parseCookieValue(\n cookieHeader: string | null | undefined,\n name: string,\n): string | undefined {\n if (!cookieHeader) return undefined;\n for (const pair of cookieHeader.split('; ')) {\n const [key, ...valueParts] = pair.split('=');\n if (key === name && valueParts.length > 0) {\n const raw = valueParts.join('=');\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Preset validation schema for cookie consent.\n * Validates the standard consent categories: necessary, analytics, marketing, functional.\n * Use this with `consent.validationSchema` in the plugin options.\n *\n * @example\n * ```ts\n * cookieConsentPlugin({\n * consentVersion: 'v1',\n * consent: { validationSchema: defaultConsentSchema },\n * })\n * ```\n */\nexport const defaultConsentSchema = z.object({\n necessary: z.boolean(),\n analytics: z.boolean(),\n marketing: z.boolean(),\n functional: z.boolean(),\n}) satisfies ConsentSchemaModel;\n\nexport const cookieConsentPlugin = <O extends CookieConsentOptions>(options: O = {} as O) => {\n return {\n id: 'cookie-consent',\n schema: getSchema(options),\n endpoints: {\n setConsent: setConsent(options),\n getConsent: getConsent(options),\n mergeConsent: mergeConsent(options),\n },\n hooks: {\n after: [\n {\n // After sign-in or sign-up (any method), merge anonymous consent to user.\n // Uses prefix matching to cover all auth methods: email, social,\n // biometrics, passkey, phone, etc.\n matcher: (context) => {\n return (\n !!context.path &&\n (context.path.startsWith('/sign-in/') || context.path.startsWith('/sign-up/'))\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n const userId = ctx.context.newSession?.user?.id;\n if (!userId) return;\n\n // Read anonymousId from the cookie set by the client\n const anonymousId = parseCookieValue(ctx.headers?.get('cookie'), ANONYMOUS_ID_COOKIE);\n if (!anonymousId) return;\n\n await mergeAnonymousConsentToUser(ctx.context.adapter, userId, anonymousId);\n }),\n },\n ],\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/cookie-consent/set', '/cookie-consent/merge'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 10,\n },\n ],\n $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\n// ─── Helper utilities ──────────────────────────────────────────────────\n\n/**\n * Extract the parsed consent object from the endpoint context.\n * Returns `null` if no consent is available.\n */\nexport async function getConsentFromCtx(ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n session?: { user?: { id?: string } } | null;\n };\n query?: { anonymousId?: string };\n}): Promise<CookieConsentRecord | null> {\n const userId = ctx.context.session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Check if a specific consent category is granted.\n */\nexport async function hasConsent(\n ctx: Parameters<typeof getConsentFromCtx>[0],\n category: string,\n): Promise<boolean> {\n const record = await getConsentFromCtx(ctx);\n if (!record) return false;\n const parsed = JSON.parse(record.consent) as Consent;\n return parsed[category] === true;\n}\n\nexport type * from './type';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["z","z"],"sources":["../src/error-codes.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({\n MISSING_ANONYMOUS_ID: 'Anonymous ID is required',\n CONSENT_NOT_FOUND: 'Cookie consent record not found',\n INVALID_CONSENT: 'Consent must be a non-empty object with boolean values',\n VERSION_MISMATCH: 'Consent version is outdated and must be renewed',\n AUTHENTICATION_REQUIRED: 'Authentication is required for this operation',\n});\n","import {\n BASE_ERROR_CODES,\n type BetterAuthOptions,\n type DBAdapter,\n type InternalLogger,\n} from 'better-auth';\nimport { APIError, createAuthEndpoint, getSessionFromCtx } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type {\n Consent,\n CookieConsentOptions,\n CookieConsentPayload,\n CookieConsentRecord,\n} from './type';\n\nconst setConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier for unauthenticated users',\n }),\n consent: z.record(z.string(), z.boolean()).meta({\n description: 'Consent preferences as category-boolean pairs',\n }),\n consentVersion: z.string().meta({\n description: 'Version of the consent policy',\n }),\n});\n\nconst getConsentSchema = z.object({\n anonymousId: z.string().optional().meta({\n description: 'Anonymous identifier fallback when no session exists',\n }),\n});\n\nconst mergeConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier to merge consent from',\n }),\n});\n\nexport const setConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/set',\n {\n method: 'POST',\n body: setConsentSchema,\n },\n async (ctx) => {\n const { anonymousId, consent, consentVersion } = ctx.body;\n\n if (!anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n if (!consent || Object.keys(consent).length === 0) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n const validatedConsent = validateConsent(options, consent, ctx.context.logger);\n\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n\n // Look for existing record by userId or anonymousId\n const existing = await findConsentRecord(ctx, userId, anonymousId);\n\n const consentJson = JSON.stringify(validatedConsent);\n const now = new Date();\n\n if (existing) {\n await ctx.context.adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: existing.id }],\n update: {\n userId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n } else {\n await ctx.context.adapter.create<CookieConsentPayload, CookieConsentRecord>({\n model: 'cookieConsent',\n data: {\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n }\n\n if (options.onConsentChange) {\n const record =\n (await findConsentRecord(ctx, userId, anonymousId)) ??\n ({\n id: '',\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n } as CookieConsentRecord);\n\n await ctx.context.runInBackgroundOrAwait(\n options.onConsentChange({ consent: record }, ctx.request),\n );\n }\n\n return ctx.json({ status: true });\n },\n );\n\nexport const getConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/get',\n {\n method: 'GET',\n query: getConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (!userId && !anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n const record = await findConsentRecord(ctx, userId, anonymousId);\n\n if (!record) {\n return ctx.json({ consent: null, versionMatch: false });\n }\n\n const currentVersion = options.consentVersion ?? 'v1';\n const versionMatch = record.consentVersion === currentVersion;\n\n return ctx.json({\n consent: {\n id: record.id,\n userId: record.userId,\n anonymousId: record.anonymousId,\n consent: JSON.parse(record.consent) as Consent,\n consentVersion: record.consentVersion,\n timestamp: record.timestamp,\n },\n versionMatch,\n });\n },\n );\n\nexport const mergeConsent = <O extends CookieConsentOptions>(_options: O) =>\n createAuthEndpoint(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: mergeConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id;\n\n if (!userId) {\n throw APIError.from('UNAUTHORIZED', COOKIE_CONSENT_ERROR_CODES.AUTHENTICATION_REQUIRED);\n }\n\n const merged = await mergeAnonymousConsentToUser(\n ctx.context.adapter,\n userId,\n ctx.body.anonymousId,\n );\n return ctx.json({ status: true, merged });\n },\n );\n\n/**\n * Merge anonymous consent into a user's record.\n * Shared between the merge endpoint and the sign-in/sign-up hook.\n *\n * @returns `true` if consent was merged, `false` if no anonymous record was found.\n */\nexport async function mergeAnonymousConsentToUser(\n adapter: DBAdapter<BetterAuthOptions>,\n userId: string,\n anonymousId: string,\n): Promise<boolean> {\n return await adapter.transaction(async (tx) => {\n const anonymousRecord = await tx.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n\n // Skip if record doesn't exist or is already linked to a user (already merged)\n if (!anonymousRecord || anonymousRecord.userId) return false;\n\n const userRecord = await tx.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n\n if (userRecord) {\n // Delete the anonymous record first so the new anonymousId can\n // be carried over without a unique-constraint conflict.\n await tx.delete({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n });\n await tx.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: userRecord.id }],\n update: {\n consent: anonymousRecord.consent,\n consentVersion: anonymousRecord.consentVersion,\n anonymousId: anonymousRecord.anonymousId,\n timestamp: new Date(),\n },\n });\n } else {\n await tx.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n update: { userId },\n });\n }\n\n return true;\n });\n}\n\n/**\n * Find a consent record by userId (preferred) or anonymousId fallback.\n */\nasync function findConsentRecord(\n ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n };\n },\n userId: string | null | undefined,\n anonymousId: string | undefined,\n): Promise<CookieConsentRecord | null> {\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Validate the consent object against the configured validation schema.\n * Returns the validated consent or the original consent when no schema is set.\n */\nfunction validateConsent(\n options: CookieConsentOptions,\n consent: Consent,\n logger: InternalLogger,\n): Consent {\n if (!options.consent?.validationSchema) {\n return consent;\n }\n const validationResult = options.consent.validationSchema['~standard'].validate(consent);\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 consent', validationResult.issues);\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n return validationResult.value as Consent;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { CookieConsentOptions } from './type';\n\nexport const cookieConsent = {\n cookieConsent: {\n fields: {\n userId: {\n type: 'string',\n required: false,\n unique: true,\n references: {\n model: 'user',\n field: 'id',\n },\n },\n anonymousId: {\n type: 'string',\n required: true,\n unique: true,\n },\n consent: {\n type: 'string',\n required: true,\n },\n consentVersion: {\n type: 'string',\n required: true,\n },\n timestamp: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n input: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends CookieConsentOptions>(options: O) => {\n return mergeSchema(cookieConsent, options.schema);\n};\n","import type { BetterAuthOptions, BetterAuthPlugin, DBAdapter } from 'better-auth';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport { getConsent, mergeAnonymousConsentToUser, mergeConsent, setConsent } from './routes';\nimport { getSchema } from './schema';\nimport type {\n Consent,\n ConsentSchemaModel,\n CookieConsentOptions,\n CookieConsentRecord,\n} from './type';\n\n/**\n * Cookie name used to store the anonymous consent ID.\n * The client sets this cookie so the server-side hook can\n * read it during sign-in to auto-merge anonymous consent.\n */\nexport const ANONYMOUS_ID_COOKIE = 'cookie-consent-anon-id';\n\n/**\n * Parse a single cookie value from a raw `Cookie` header string.\n */\nfunction parseCookieValue(\n cookieHeader: string | null | undefined,\n name: string,\n): string | undefined {\n if (!cookieHeader) return undefined;\n for (const pair of cookieHeader.split('; ')) {\n const [key, ...valueParts] = pair.split('=');\n if (key === name && valueParts.length > 0) {\n const raw = valueParts.join('=');\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Preset validation schema for cookie consent.\n * Validates the standard consent categories: necessary, analytics, marketing, functional.\n * Use this with `consent.validationSchema` in the plugin options.\n *\n * @example\n * ```ts\n * cookieConsentPlugin({\n * consentVersion: 'v1',\n * consent: { validationSchema: defaultConsentSchema },\n * })\n * ```\n */\nexport const defaultConsentSchema = z.object({\n necessary: z.boolean(),\n analytics: z.boolean(),\n marketing: z.boolean(),\n functional: z.boolean(),\n}) satisfies ConsentSchemaModel;\n\nexport const cookieConsentPlugin = <O extends CookieConsentOptions>(options: O = {} as O) => {\n return {\n id: 'cookie-consent',\n schema: getSchema(options),\n endpoints: {\n setConsent: setConsent(options),\n getConsent: getConsent(options),\n mergeConsent: mergeConsent(options),\n },\n hooks: {\n after: [\n {\n // After sign-in or sign-up (any method), merge anonymous consent to user.\n // Uses prefix matching to cover all auth methods: email, social,\n // biometrics, passkey, phone, etc.\n matcher: (context) => {\n return (\n !!context.path &&\n (context.path.startsWith('/sign-in/') || context.path.startsWith('/sign-up/'))\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n const userId = ctx.context.newSession?.user?.id;\n if (!userId) return;\n\n // Read anonymousId from the cookie set by the client\n const anonymousId = parseCookieValue(ctx.headers?.get('cookie'), ANONYMOUS_ID_COOKIE);\n if (!anonymousId) return;\n\n await mergeAnonymousConsentToUser(ctx.context.adapter, userId, anonymousId);\n }),\n },\n ],\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/cookie-consent/set', '/cookie-consent/merge'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 10,\n },\n ],\n $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\n// ─── Helper utilities ──────────────────────────────────────────────────\n\n/**\n * Extract the parsed consent object from the endpoint context.\n * Returns `null` if no consent is available.\n */\nexport async function getConsentFromCtx(ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n session?: { user?: { id?: string } } | null;\n };\n query?: { anonymousId?: string };\n}): Promise<CookieConsentRecord | null> {\n const userId = ctx.context.session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Check if a specific consent category is granted.\n */\nexport async function hasConsent(\n ctx: Parameters<typeof getConsentFromCtx>[0],\n category: string,\n): Promise<boolean> {\n const record = await getConsentFromCtx(ctx);\n if (!record) return false;\n const parsed = JSON.parse(record.consent) as Consent;\n return parsed[category] === true;\n}\n\nexport type * from './type';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth-cookie-consent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-dev.0",
|
|
4
4
|
"description": "Better Auth Cookie Consent plugin for GDPR-compliant consent management",
|
|
5
5
|
"homepage": "https://github.com/marcjulian/better-auth-plugins#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/marcjulian/better-auth-plugins/issues"
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"author": "
|
|
10
|
+
"author": "Gary Großgarten <info@gary.dev>",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
13
|
"url": "git+https://github.com/marcjulian/better-auth-plugins.git"
|
|
@@ -25,23 +25,26 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"
|
|
29
|
-
"zod": "^4.3.6"
|
|
28
|
+
"zod": "^4.4.3"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^25.
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
31
|
+
"@types/node": "^25.6.2",
|
|
32
|
+
"better-auth": "1.7.5",
|
|
33
|
+
"bumpp": "^11.1.0",
|
|
34
|
+
"nanostores": "^1.5.1",
|
|
35
|
+
"tsdown": "^0.22.14",
|
|
36
|
+
"typescript": "^7.0.2",
|
|
37
|
+
"vitest": "^4.1.11"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
39
|
-
"better-auth": "^1.5
|
|
40
|
+
"better-auth": "^1.7.5",
|
|
41
|
+
"nanostores": "^1.5.0"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
42
44
|
"build": "tsdown",
|
|
43
45
|
"dev": "tsdown --watch",
|
|
44
46
|
"test": "vitest",
|
|
45
|
-
"typecheck": "tsc --noEmit"
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"release": "bumpp"
|
|
46
49
|
}
|
|
47
50
|
}
|