better-auth-vercel-geolocation 1.1.1 → 1.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # better-auth-vercel-geolocation
2
2
 
3
- A [better-auth](https://www.better-auth.com/) plugin that enriches the auth session with Vercel edge geolocation data. On session create and refresh, it reads the [`x-vercel-ip-*` headers](https://vercel.com/docs/headers/request-headers) via `@vercel/functions` server-side and writes the configured fields into the session record — no browser permission prompt, no extra API call.
3
+ A [better-auth](https://www.better-auth.com/) plugin that enriches the auth session with Vercel edge geolocation data. On session create and refresh, it reads the `[x-vercel-ip-*` headers](https://vercel.com/docs/headers/request-headers) server-side and writes the configured fields into the session record — no browser permission prompt, no extra API call.
4
4
 
5
5
  > [!NOTE]
6
6
  > Requires deployment on Vercel. In local development, geo fields will be `undefined`.
@@ -55,18 +55,37 @@ The session now includes geolocation fields:
55
55
  const { data: session } = authClient.useSession();
56
56
 
57
57
  session.city; // "Hamburg"
58
- session.country; // "HH"
59
- session.countryRegion; // "BE"
58
+ session.country; // "DE"
59
+ session.countryRegion; // "HH"
60
60
  session.flag; // "🇩🇪"
61
61
  ```
62
62
 
63
63
  ## Options
64
64
 
65
+
65
66
  | Option | Type | Default | Description |
66
67
  | ----------------- | ------------------------ | ---------------------------------------- | --------------------------------------------- |
67
68
  | `fields` | `GeolocationFieldConfig` | `{ city, country, countryRegion, flag }` | Which fields to store on the session |
68
69
  | `updateOnRefresh` | `boolean` | `true` | Update geo data when the session is refreshed |
69
70
 
71
+
72
+ ## Available fields
73
+
74
+
75
+ | Field | Header | Example | Default |
76
+ | --------------- | ------------------------------ | ----------------- | ------- |
77
+ | `city` | `x-vercel-ip-city` | `"Hamburg"` | ✅ |
78
+ | `country` | `x-vercel-ip-country` | `"DE"` | ✅ |
79
+ | `countryRegion` | `x-vercel-ip-country-region` | `"HH"` | ✅ |
80
+ | `flag` | *(derived from country)* | `"🇩🇪"` | ✅ |
81
+ | `latitude` | `x-vercel-ip-latitude` | `"53.5573"` | |
82
+ | `longitude` | `x-vercel-ip-longitude` | `"9.9652"` | |
83
+ | `postalCode` | `x-vercel-ip-postal-code` | `"20359"` | |
84
+ | `region` | *(Vercel Edge Network region)* | `"fra1"` | |
85
+ | `timezone` | `x-vercel-ip-timezone` | `"Europe/Berlin"` | |
86
+
87
+
88
+
70
89
  ## License
71
90
 
72
- MIT
91
+ MIT
package/dist/client.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as better_auth from 'better-auth';
2
2
  import { Geo } from '@vercel/functions';
3
3
 
4
- type GeolocationField = keyof Geo;
4
+ type GeolocationField = keyof Geo | 'timezone';
5
5
  /**
6
6
  * Configuration for which geolocation fields to store.
7
7
  * Each field can be set to `true` to include it in the session.
@@ -23,6 +23,8 @@ interface GeolocationFieldConfig {
23
23
  postalCode?: boolean;
24
24
  /** The Vercel Edge Network region that received the request. */
25
25
  region?: boolean;
26
+ /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */
27
+ timezone?: boolean;
26
28
  }
27
29
  declare const DEFAULT_FIELDS: {
28
30
  readonly city: true;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.cts","sources":["../src/types.ts","../src/geolocation.ts","../src/client.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo;\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] = geo[field];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};","import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport type { geolocation } from './geolocation';\nimport type {\n DefaultGeolocationFields,\n GeolocationFieldConfig,\n} from './types';\n\nexport const geolocationClient = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>() => {\n return {\n id: 'vercel-geolocation',\n $InferServerPlugin: {} as ReturnType<typeof geolocation<T>>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/CO,cAAA,iBAAA,mBAAA,sBAAA,GAAA,wBAAA;AACP;AACA,wBAAA,UAAA,QAAA,WAAA;AACA;;;;"}
1
+ {"version":3,"file":"client.d.cts","sources":["../src/types.ts","../src/geolocation.ts","../src/client.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo | 'timezone';\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */\n timezone?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo , } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] =\n field === 'timezone'\n ? (request.headers.get('x-vercel-ip-timezone') ?? undefined)\n : geo[field as keyof typeof geo];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};","import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport type { geolocation } from './geolocation';\nimport type {\n DefaultGeolocationFields,\n GeolocationFieldConfig,\n} from './types';\n\nexport const geolocationClient = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>() => {\n return {\n id: 'vercel-geolocation',\n $InferServerPlugin: {} as ReturnType<typeof geolocation<T>>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/CO,cAAA,iBAAA,mBAAA,sBAAA,GAAA,wBAAA;AACP;AACA,wBAAA,UAAA,QAAA,WAAA;AACA;;;;"}
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as better_auth from 'better-auth';
2
2
  import { Geo } from '@vercel/functions';
3
3
 
4
- type GeolocationField = keyof Geo;
4
+ type GeolocationField = keyof Geo | 'timezone';
5
5
  /**
6
6
  * Configuration for which geolocation fields to store.
7
7
  * Each field can be set to `true` to include it in the session.
@@ -23,6 +23,8 @@ interface GeolocationFieldConfig {
23
23
  postalCode?: boolean;
24
24
  /** The Vercel Edge Network region that received the request. */
25
25
  region?: boolean;
26
+ /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */
27
+ timezone?: boolean;
26
28
  }
27
29
  declare const DEFAULT_FIELDS: {
28
30
  readonly city: true;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sources":["../src/types.ts","../src/geolocation.ts","../src/client.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo;\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] = geo[field];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};","import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport type { geolocation } from './geolocation';\nimport type {\n DefaultGeolocationFields,\n GeolocationFieldConfig,\n} from './types';\n\nexport const geolocationClient = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>() => {\n return {\n id: 'vercel-geolocation',\n $InferServerPlugin: {} as ReturnType<typeof geolocation<T>>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/CO,cAAA,iBAAA,mBAAA,sBAAA,GAAA,wBAAA;AACP;AACA,wBAAA,UAAA,QAAA,WAAA;AACA;;;;"}
1
+ {"version":3,"file":"client.d.ts","sources":["../src/types.ts","../src/geolocation.ts","../src/client.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo | 'timezone';\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */\n timezone?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo , } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] =\n field === 'timezone'\n ? (request.headers.get('x-vercel-ip-timezone') ?? undefined)\n : geo[field as keyof typeof geo];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};","import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport type { geolocation } from './geolocation';\nimport type {\n DefaultGeolocationFields,\n GeolocationFieldConfig,\n} from './types';\n\nexport const geolocationClient = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>() => {\n return {\n id: 'vercel-geolocation',\n $InferServerPlugin: {} as ReturnType<typeof geolocation<T>>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/CO,cAAA,iBAAA,mBAAA,sBAAA,GAAA,wBAAA;AACP;AACA,wBAAA,UAAA,QAAA,WAAA;AACA;;;;"}
package/dist/index.cjs CHANGED
@@ -25,7 +25,7 @@ const geolocation = (options)=>{
25
25
  if (!request) return;
26
26
  const geo = functions.geolocation(request);
27
27
  for (const field of activeFields){
28
- session[field] = geo[field];
28
+ session[field] = field === 'timezone' ? request.headers.get('x-vercel-ip-timezone') ?? undefined : geo[field];
29
29
  }
30
30
  return {
31
31
  data: session
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as better_auth from 'better-auth';
2
2
  import { Geo } from '@vercel/functions';
3
3
 
4
- type GeolocationField = keyof Geo;
4
+ type GeolocationField = keyof Geo | 'timezone';
5
5
  /**
6
6
  * Configuration for which geolocation fields to store.
7
7
  * Each field can be set to `true` to include it in the session.
@@ -23,6 +23,8 @@ interface GeolocationFieldConfig {
23
23
  postalCode?: boolean;
24
24
  /** The Vercel Edge Network region that received the request. */
25
25
  region?: boolean;
26
+ /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */
27
+ timezone?: boolean;
26
28
  }
27
29
  declare const DEFAULT_FIELDS: {
28
30
  readonly city: true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/geolocation.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo;\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] = geo[field];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
1
+ {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/geolocation.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo | 'timezone';\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */\n timezone?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo , } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] =\n field === 'timezone'\n ? (request.headers.get('x-vercel-ip-timezone') ?? undefined)\n : geo[field as keyof typeof geo];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as better_auth from 'better-auth';
2
2
  import { Geo } from '@vercel/functions';
3
3
 
4
- type GeolocationField = keyof Geo;
4
+ type GeolocationField = keyof Geo | 'timezone';
5
5
  /**
6
6
  * Configuration for which geolocation fields to store.
7
7
  * Each field can be set to `true` to include it in the session.
@@ -23,6 +23,8 @@ interface GeolocationFieldConfig {
23
23
  postalCode?: boolean;
24
24
  /** The Vercel Edge Network region that received the request. */
25
25
  region?: boolean;
26
+ /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */
27
+ timezone?: boolean;
26
28
  }
27
29
  declare const DEFAULT_FIELDS: {
28
30
  readonly city: true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/geolocation.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo;\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] = geo[field];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/geolocation.ts"],"sourcesContent":["import type { Geo } from '@vercel/functions';\n\nexport type GeolocationField = keyof Geo | 'timezone';\n\n/**\n * Configuration for which geolocation fields to store.\n * Each field can be set to `true` to include it in the session.\n */\nexport interface GeolocationFieldConfig {\n /** The city that the request originated from. */\n city?: boolean;\n /** The country that the request originated from. */\n country?: boolean;\n /** The region part of the ISO 3166-2 code of the client IP. */\n countryRegion?: boolean;\n /** The flag emoji for the country the request originated from. */\n flag?: boolean;\n /** The latitude of the client. */\n latitude?: boolean;\n /** The longitude of the client. */\n longitude?: boolean;\n /** The postal code of the client. */\n postalCode?: boolean;\n /** The Vercel Edge Network region that received the request. */\n region?: boolean;\n /** The name of the time zone for the location of the requester's public IP address in ICANN Time Zone Database name format such as America/Chicago. */\n timezone?: boolean;\n}\n\nexport const DEFAULT_FIELDS = {\n city: true,\n country: true,\n countryRegion: true,\n flag: true,\n} as const satisfies GeolocationFieldConfig;\n\nexport type DefaultGeolocationFields = typeof DEFAULT_FIELDS;\n\nexport type GeoSchemaFields<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true ? K : never]: {\n type: 'string';\n required: false;\n input: false;\n };\n};\n\nexport type InferGeoSession<T extends GeolocationFieldConfig> = {\n [K in keyof T & GeolocationField as T[K] extends true\n ? K\n : never]?: string | null;\n};\n\nexport interface GeolocationPluginOptions<\n T extends GeolocationFieldConfig = GeolocationFieldConfig,\n> {\n /**\n * Which geolocation fields to store on the session.\n * All available fields: city, country, countryRegion, flag, latitude, longitude, postalCode, region.\n * @default { city: true, country: true, countryRegion: true, flag: true }\n */\n fields?: T & GeolocationFieldConfig;\n /**\n * Whether to update session geolocation data when the session is refreshed.\n * @default true\n */\n updateOnRefresh?: boolean;\n}\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { geolocation as extractGeo , } from '@vercel/functions';\n\nimport {\n DEFAULT_FIELDS,\n type DefaultGeolocationFields,\n type GeolocationField,\n type GeolocationFieldConfig,\n type GeolocationPluginOptions,\n type GeoSchemaFields,\n type InferGeoSession,\n} from './types';\n\nexport const geolocation = <\n const T extends GeolocationFieldConfig = DefaultGeolocationFields,\n>(\n options?: GeolocationPluginOptions<T>,\n) => {\n const updateOnRefresh = options?.updateOnRefresh ?? true;\n const fieldConfig = (options?.fields ?? DEFAULT_FIELDS) as T;\n\n const activeFields = (Object.keys(fieldConfig) as GeolocationField[]).filter(\n (field) => fieldConfig[field] === true,\n );\n\n const schemaFields = Object.fromEntries(\n activeFields.map((field) => [\n field,\n {\n type: 'string',\n required: false,\n input: false,\n },\n ]),\n ) as GeoSchemaFields<T>;\n\n function applyGeo(\n session: Record<string, unknown>,\n request: Request | undefined,\n ) {\n if (!request) return;\n\n const geo = extractGeo(request);\n for (const field of activeFields) {\n session[field] =\n field === 'timezone'\n ? (request.headers.get('x-vercel-ip-timezone') ?? undefined)\n : geo[field as keyof typeof geo];\n }\n return { data: session };\n }\n\n return {\n id: 'vercel-geolocation',\n schema: {\n session: { fields: schemaFields },\n },\n $Infer: {\n Session: {\n session: {} as InferGeoSession<T>,\n },\n },\n init() {\n return {\n options: {\n databaseHooks: {\n session: {\n create: {\n before: async (session, ctx) => {\n return applyGeo(session, ctx?.request);\n },\n },\n update: {\n before: async (session, ctx) => {\n if (!updateOnRefresh) return;\n return applyGeo(session, ctx?.request);\n },\n },\n },\n },\n },\n };\n },\n } satisfies BetterAuthPlugin;\n};"],"names":[],"mappings":";;;AACO,KAAA,gBAAA,SAAA,GAAA;AACP;AACA;AACA;AACA;AACO,UAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,cAAA;AACP;AACA;AACA;AACA;AACA;AACO,KAAA,wBAAA,UAAA,cAAA;AACA,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,eAAA,WAAA,sBAAA;AACP,oBAAA,gBAAA;AACA;AACO,UAAA,wBAAA,WAAA,sBAAA,GAAA,sBAAA;AACP;AACA;AACA;AACA;AACA;AACA,iBAAA,sBAAA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtDO,cAAA,WAAA,mBAAA,sBAAA,GAAA,wBAAA,YAAA,wBAAA;AACP;AACA;AACA;AACA,oBAAA,eAAA;AACA;AACA;AACA;AACA;AACA,qBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,4BAAA,MAAA,wBAA0D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAC/E,kCAAA,MAAA;AACA;AACA;AACA;AACA,0CAAA,OAAA;AACA;AACA,uCAAA,IAAA;AACA,uCAAA,IAAA;AACA;AACA,uCAAA,IAAA;AACA;AACA;AACA;AACA,6BAAA,MAAA,wBAA2D,WAAqB,CAAA,sBAAA,YAAA,OAAA;AAChF,kCAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ const geolocation = (options)=>{
23
23
  if (!request) return;
24
24
  const geo = geolocation$1(request);
25
25
  for (const field of activeFields){
26
- session[field] = geo[field];
26
+ session[field] = field === 'timezone' ? request.headers.get('x-vercel-ip-timezone') ?? undefined : geo[field];
27
27
  }
28
28
  return {
29
29
  data: session
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth-vercel-geolocation",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A better-auth plugin that enriches sessions with Vercel edge geolocation data.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/weepaho3/better-auth-vercel-geolocation",