Skip to content

Commit 6e4516a

Browse files
feat: introduce @auth/neo4j-adapter (#7804)
Database adapters are not dependent on Next.js features, so it makes sense to republish them under the `@auth/*` scope. This PR is part of a series to convert adapters, using `@auth/core` for types. BREAKING CHANGE: If you are coming from the previous adapter, change your `package.json`: ```diff - "@next-auth/neo4j-adapter": "0.0.0", + "@auth/neo4j-adapter": "0.0.0", ``` And run `npm install`, `yarn install` or `pnpm install` respectively. **Note:** This packages is published as ESM-only This package assumes that `globalThis.crypto` is available. In older Node.js versions, you can polyfill by adding: `globalThis.crypto ??= require("node:crypto").webcrypto`
1 parent 8a0b11f commit 6e4516a

File tree

10 files changed

+130
-113
lines changed

10 files changed

+130
-113
lines changed

.github/ISSUE_TEMPLATE/3_bug_adapter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ body:
2727
- "@auth/firebase-adapter"
2828
- "@auth/mikro-orm-adapter"
2929
- "@auth/mongodb-adapter"
30-
- "@next-auth/neo4j-adapter"
30+
- "@auth/neo4j-adapter"
3131
- "@next-auth/pouchdb-adapter"
3232
- "@auth/prisma-adapter"
3333
- "@next-auth/sequelize-adapter"

.github/issue-labeler.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ mongodb:
1919
- "@auth/mongodb-adapter"
2020

2121
neo4j:
22-
- "@next-auth/neo4j-adapter"
22+
- "@auth/neo4j-adapter"
2323

2424
pouchdb:
2525
- "@next-auth/pouchdb-adapter"

packages/adapter-neo4j/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
</a>
99
<h3 align="center"><b>Neo4j Adapter</b> - NextAuth.js / Auth.js</a></h3>
1010
<p align="center" style="align: center;">
11-
<a href="https://npm.im/@next-auth/neo4j-adapter">
11+
<a href="https://npm.im/@auth/neo4j-adapter">
1212
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
1313
</a>
14-
<a href="https://npm.im/@next-auth/neo4j-adapter">
15-
<img alt="npm" src="https://img.shields.io/npm/v/@next-auth/neo4j-adapter?color=green&label=@next-auth/neo4j-adapter&style=flat-square">
14+
<a href="https://npm.im/@auth/neo4j-adapter">
15+
<img alt="npm" src="https://img.shields.io/npm/v/@auth/neo4j-adapter?color=green&label=@auth/neo4j-adapter&style=flat-square">
1616
</a>
17-
<a href="https://www.npmtrends.com/@next-auth/neo4j-adapter">
18-
<img src="https://img.shields.io/npm/dm/@next-auth/neo4j-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
17+
<a href="https://www.npmtrends.com/@auth/neo4j-adapter">
18+
<img src="https://img.shields.io/npm/dm/@auth/neo4j-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
1919
</a>
2020
<a href="https://github.com/nextauthjs/next-auth/stargazers">
2121
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />

packages/adapter-neo4j/package.json

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "@next-auth/neo4j-adapter",
3-
"version": "1.0.6",
4-
"description": "neo4j adapter for next-auth.",
2+
"name": "@auth/neo4j-adapter",
3+
"version": "0.0.0",
4+
"description": "neo4j adapter for Auth.js",
55
"homepage": "https://authjs.dev",
66
"repository": "https://github.com/nextauthjs/next-auth",
77
"bugs": {
@@ -11,7 +11,19 @@
1111
"contributors": [
1212
"Balázs Orbán <[email protected]>"
1313
],
14-
"main": "dist/index.js",
14+
"type": "module",
15+
"types": "./index.d.ts",
16+
"files": [
17+
"*.js",
18+
"*.d.ts*",
19+
"src"
20+
],
21+
"exports": {
22+
".": {
23+
"types": "./index.d.ts",
24+
"import": "./index.js"
25+
}
26+
},
1527
"license": "ISC",
1628
"keywords": [
1729
"next-auth",
@@ -28,26 +40,20 @@
2840
"test": "./tests/test.sh",
2941
"build": "tsc"
3042
},
31-
"files": [
32-
"README.md",
33-
"dist"
34-
],
3543
"peerDependencies": {
36-
"neo4j-driver": "^4.0.0 || ^5.7.0",
37-
"next-auth": "^4"
44+
"neo4j-driver": "^4.0.0 || ^5.7.0"
3845
},
3946
"devDependencies": {
4047
"@next-auth/adapter-test": "workspace:*",
4148
"@next-auth/tsconfig": "workspace:*",
4249
"@types/uuid": "^8.3.3",
4350
"jest": "^27.4.3",
44-
"neo4j-driver": "^5.7.0",
45-
"next-auth": "workspace:*"
51+
"neo4j-driver": "^5.7.0"
4652
},
4753
"dependencies": {
48-
"uuid": "^8.3.2"
54+
"@auth/core": "workspace:*"
4955
},
5056
"jest": {
5157
"preset": "@next-auth/adapter-test/jest"
5258
}
53-
}
59+
}

packages/adapter-neo4j/src/index.ts

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,13 @@
99
* ## Installation
1010
*
1111
* ```bash npm2yarn2pnpm
12-
* npm install next-auth @next-auth/neo4j-adapter neo4j-driver
12+
* npm install @auth/neo4j-adapter neo4j-driver
1313
* ```
1414
*
15-
* @module @next-auth/neo4j-adapter
15+
* @module @auth/neo4j-adapter
1616
*/
17-
import type { Session } from "neo4j-driver"
18-
import type { Adapter } from "next-auth/adapters"
19-
import { v4 as uuid } from "uuid"
20-
21-
import { client, format } from "./utils"
22-
export { format }
17+
import { type Session, isInt, integer } from "neo4j-driver"
18+
import type { Adapter } from "@auth/core/adapters"
2319

2420
/** This is the interface of the Neo4j adapter options. The Neo4j adapter takes a {@link https://neo4j.com/docs/bolt/current/driver-api/#driver-session Neo4j session} as its only argument. */
2521
export interface Neo4jOptions extends Session {}
@@ -31,7 +27,7 @@ export interface Neo4jOptions extends Session {}
3127
*
3228
* ```javascript title="pages/api/auth/[...nextauth].js"
3329
* import neo4j from "neo4j-driver"
34-
* import { Neo4jAdapter } from "@next-auth/neo4j-adapter"
30+
* import { Neo4jAdapter } from "@auth/neo4j-adapter"
3531
*
3632
* const driver = neo4j.driver(
3733
* "bolt://localhost",
@@ -134,7 +130,7 @@ export function Neo4jAdapter(session: Session): Adapter {
134130

135131
return {
136132
async createUser(data) {
137-
const user: any = { id: uuid(), ...data }
133+
const user = { id: crypto.randomUUID(), ...data }
138134
await write(`CREATE (u:User $data)`, user)
139135
return user
140136
},
@@ -288,3 +284,69 @@ export function Neo4jAdapter(session: Session): Adapter {
288284
},
289285
}
290286
}
287+
288+
// https://github.com/honeinc/is-iso-date/blob/master/index.js
289+
const isoDateRE =
290+
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
291+
292+
function isDate(value: any) {
293+
return value && isoDateRE.test(value) && !isNaN(Date.parse(value))
294+
}
295+
296+
export const format = {
297+
/** Takes a plain old JavaScript object and turns it into a Neo4j compatible object */
298+
to(object: Record<string, any>) {
299+
const newObject: Record<string, unknown> = {}
300+
for (const key in object) {
301+
const value = object[key]
302+
if (value instanceof Date) newObject[key] = value.toISOString()
303+
else newObject[key] = value
304+
}
305+
return newObject
306+
},
307+
/** Takes a Neo4j object and returns a plain old JavaScript object */
308+
from<T = Record<string, unknown>>(object?: Record<string, any>): T | null {
309+
const newObject: Record<string, unknown> = {}
310+
if (!object) return null
311+
for (const key in object) {
312+
const value = object[key]
313+
if (isDate(value)) {
314+
newObject[key] = new Date(value)
315+
} else if (isInt(value)) {
316+
if (integer.inSafeRange(value)) newObject[key] = value.toNumber()
317+
else newObject[key] = value.toString()
318+
} else {
319+
newObject[key] = value
320+
}
321+
}
322+
323+
return newObject as T
324+
},
325+
}
326+
327+
function client(session: Session) {
328+
return {
329+
/** Reads values from the database */
330+
async read<T>(statement: string, values?: any): Promise<T | null> {
331+
const result = await session.readTransaction((tx) =>
332+
tx.run(statement, values)
333+
)
334+
335+
return format.from<T>(result?.records[0]?.get(0)) ?? null
336+
},
337+
/**
338+
* Reads/writes values from/to the database.
339+
* Properties are available under `$data`
340+
*/
341+
async write<T extends Record<string, any>>(
342+
statement: string,
343+
values: T
344+
): Promise<any> {
345+
const result = await session.writeTransaction((tx) =>
346+
tx.run(statement, { data: format.to(values) })
347+
)
348+
349+
return format.from<T>(result?.records[0]?.toObject())
350+
},
351+
}
352+
}

packages/adapter-neo4j/src/utils.ts

Lines changed: 0 additions & 68 deletions
This file was deleted.

packages/adapter-neo4j/tests/index.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import statements from "./resources/statements"
44

55
import { Neo4jAdapter, format } from "../src"
66

7+
globalThis.crypto ??= require("node:crypto").webcrypto
8+
79
const driver = neo4j.driver(
810
"bolt://localhost",
911
neo4j.auth.basic("neo4j", "password")

packages/adapter-neo4j/tsconfig.json

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
11
{
2-
"extends": "@next-auth/tsconfig/tsconfig.adapters.json",
2+
"extends": "@next-auth/tsconfig/tsconfig.base.json",
33
"compilerOptions": {
4+
"allowJs": true,
5+
"baseUrl": ".",
6+
"isolatedModules": true,
7+
"target": "ES2020",
8+
"module": "ESNext",
9+
"moduleResolution": "node",
10+
"outDir": ".",
411
"rootDir": "src",
5-
"outDir": "dist"
12+
"skipDefaultLibCheck": true,
13+
"strictNullChecks": true,
14+
"stripInternal": true,
15+
"declarationMap": true,
16+
"declaration": true
617
},
7-
"exclude": ["tests", "dist", "jest.config.js"]
8-
}
18+
"include": [
19+
"src/**/*"
20+
],
21+
"exclude": [
22+
"*.js",
23+
"*.d.ts",
24+
]
25+
}

pnpm-lock.yaml

Lines changed: 6 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)