better-auth-cookie-consent 0.2.0 → 0.3.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 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/dist/client.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import { n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema } from "./index-B9HQT45G.mjs";
1
+ import { n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema } from "./index-B6ZS0bF2.mjs";
2
2
  import z from "zod";
3
-
3
+ import { BetterFetch } from "better-auth/client";
4
4
  //#region src/client.d.ts
5
5
  /**
6
6
  * Client-side consent state, kept in sync with the server.
7
7
  */
8
- interface ConsentState<TConsent extends Consent = Consent> {
8
+ export interface ConsentState<TConsent extends Consent = Consent> {
9
9
  consent: TConsent | null;
10
10
  consentVersion: string | null;
11
11
  versionMatch: boolean;
@@ -13,7 +13,7 @@ interface ConsentState<TConsent extends Consent = Consent> {
13
13
  /**
14
14
  * Inferred type from the default consent schema.
15
15
  */
16
- type DefaultConsentModel = z.infer<typeof defaultConsentSchema>;
16
+ export type DefaultConsentModel = z.infer<typeof defaultConsentSchema>;
17
17
  /**
18
18
  * Client plugin for cookie consent management.
19
19
  *
@@ -28,13 +28,15 @@ type DefaultConsentModel = z.infer<typeof defaultConsentSchema>;
28
28
  * });
29
29
  * ```
30
30
  */
31
- declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
31
+ export declare const cookieConsentClient: <TConsent extends Consent = {
32
+ [x: string]: boolean;
33
+ }>() => {
32
34
  id: "cookie-consent";
33
35
  $InferServerPlugin: ReturnType<typeof cookieConsentPlugin>;
34
- getAtoms($fetch: import("better-auth/client").BetterFetch): {
36
+ getAtoms(): {
35
37
  $consent: import("nanostores").PreinitializedWritableAtom<ConsentState<TConsent>> & object;
36
38
  };
37
- getActions($fetch: import("better-auth/client").BetterFetch, $store: import("better-auth").ClientStore): {
39
+ getActions($fetch: BetterFetch, $store: import("better-auth").ClientStore): {
38
40
  cookieConsent: {
39
41
  /**
40
42
  * Set consent preferences on the server.
@@ -53,7 +55,7 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
53
55
  } | {
54
56
  data: null;
55
57
  error: {
56
- message?: string | undefined;
58
+ message?: string;
57
59
  status: number;
58
60
  statusText: string;
59
61
  };
@@ -62,13 +64,6 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
62
64
  * Retrieve consent from the server.
63
65
  */
64
66
  getConsent: (anonymousId?: string) => Promise<{
65
- data: null;
66
- error: {
67
- message?: string | undefined;
68
- status: number;
69
- statusText: string;
70
- };
71
- } | {
72
67
  data: {
73
68
  consent: {
74
69
  id: string;
@@ -81,27 +76,33 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
81
76
  versionMatch: boolean;
82
77
  };
83
78
  error: null;
84
- }>;
85
- /**
86
- * Merge anonymous consent into the authenticated user's record.
87
- */
88
- mergeConsent: (anonymousId: string) => Promise<{
79
+ } | {
89
80
  data: null;
90
81
  error: {
91
- message?: string | undefined;
82
+ message?: string;
92
83
  status: number;
93
84
  statusText: string;
94
85
  };
95
- } | {
86
+ }>;
87
+ /**
88
+ * Merge anonymous consent into the authenticated user's record.
89
+ */
90
+ mergeConsent: (anonymousId: string) => Promise<{
96
91
  data: {
97
92
  status: boolean;
98
93
  merged: boolean;
99
94
  };
100
95
  error: null;
96
+ } | {
97
+ data: null;
98
+ error: {
99
+ message?: string;
100
+ status: number;
101
+ statusText: string;
102
+ };
101
103
  }>;
102
104
  };
103
105
  };
104
106
  };
105
107
  //#endregion
106
- export { ConsentState, DefaultConsentModel, cookieConsentClient };
107
108
  //# sourceMappingURL=client.d.mts.map
package/dist/client.mjs CHANGED
@@ -18,7 +18,7 @@ const cookieConsentClient = () => {
18
18
  return {
19
19
  id: "cookie-consent",
20
20
  $InferServerPlugin: {},
21
- getAtoms($fetch) {
21
+ getAtoms() {
22
22
  return { $consent: atom({
23
23
  consent: null,
24
24
  consentVersion: null,
@@ -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';\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($fetch) {\n const $consent = atom<ConsentState<TConsent>>({\n consent: null,\n consentVersion: null,\n versionMatch: false,\n });\n return { $consent };\n },\n\n getActions($fetch, $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,SAAS,QAAQ;GAMf,OAAO,EAAE,UALQ,KAA6B;IAC5C,SAAS;IACT,gBAAgB;IAChB,cAAc;GAChB,CACgB,EAAE;EACpB;EAEA,WAAW,QAAQ,QAAQ;GACzB,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
+ {"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,6 +1,5 @@
1
1
  import * as z$1 from "zod";
2
2
  import { BetterAuthOptions, DBAdapter, InferOptionSchema, StandardSchemaV1 } from "better-auth";
3
-
4
3
  //#region src/schema.d.ts
5
4
  declare const cookieConsent: {
6
5
  cookieConsent: {
@@ -232,7 +231,7 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
232
231
  hooks: {
233
232
  after: {
234
233
  matcher: (context: import("better-auth").HookEndpointContext) => boolean;
235
- handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>;
234
+ handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
236
235
  }[];
237
236
  };
238
237
  options: NoInfer<O>;
@@ -242,11 +241,11 @@ declare const cookieConsentPlugin: <O extends CookieConsentOptions>(options?: O)
242
241
  max: number;
243
242
  }[];
244
243
  $ERROR_CODES: {
245
- MISSING_ANONYMOUS_ID: import("better-auth").RawError<"MISSING_ANONYMOUS_ID">;
244
+ AUTHENTICATION_REQUIRED: import("better-auth").RawError<"AUTHENTICATION_REQUIRED">;
246
245
  CONSENT_NOT_FOUND: import("better-auth").RawError<"CONSENT_NOT_FOUND">;
247
246
  INVALID_CONSENT: import("better-auth").RawError<"INVALID_CONSENT">;
247
+ MISSING_ANONYMOUS_ID: import("better-auth").RawError<"MISSING_ANONYMOUS_ID">;
248
248
  VERSION_MISMATCH: import("better-auth").RawError<"VERSION_MISMATCH">;
249
- AUTHENTICATION_REQUIRED: import("better-auth").RawError<"AUTHENTICATION_REQUIRED">;
250
249
  };
251
250
  };
252
251
  /**
@@ -272,4 +271,4 @@ declare function getConsentFromCtx(ctx: {
272
271
  declare function hasConsent(ctx: Parameters<typeof getConsentFromCtx>[0], category: string): Promise<boolean>;
273
272
  //#endregion
274
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 };
275
- //# sourceMappingURL=index-B9HQT45G.d.mts.map
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-B9HQT45G.mjs";
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
2
  export { ANONYMOUS_ID_COOKIE, type Consent, type ConsentSchemaModel, type CookieConsentOptions, type CookieConsentPayload, type CookieConsentRecord, cookieConsentPlugin, defaultConsentSchema, getConsentFromCtx, hasConsent };
@@ -1 +1 @@
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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"}
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.2.0",
3
+ "version": "0.3.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": "Marc Stammerjohann <me@marcjulian.de>",
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
- "nanostores": "^1.1.1",
29
- "zod": "^4.4.3"
28
+ "zod": "4.6.5"
30
29
  },
31
30
  "devDependencies": {
32
- "@types/node": "^25.3.5",
33
- "bumpp": "^10.4.1",
34
- "tsdown": "^0.22.0",
35
- "typescript": "^5.9.3",
36
- "vitest": "^4.0.18"
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.23.0",
36
+ "typescript": "7.0.2",
37
+ "vitest": "4.1.11"
37
38
  },
38
39
  "peerDependencies": {
39
- "better-auth": "1.6.11"
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
  }