@cyguin/notify 0.1.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/.github/workflows/publish.yml +27 -0
- package/LICENSE +7 -0
- package/README.md +152 -0
- package/dist/adapters/postgres.cjs +7 -0
- package/dist/adapters/postgres.d.cts +5 -0
- package/dist/adapters/postgres.d.ts +5 -0
- package/dist/adapters/postgres.js +7 -0
- package/dist/adapters/sqlite.cjs +9 -0
- package/dist/adapters/sqlite.d.cts +6 -0
- package/dist/adapters/sqlite.d.ts +6 -0
- package/dist/adapters/sqlite.js +9 -0
- package/dist/chunk-4SP667TN.js +33 -0
- package/dist/chunk-DBFBHOZI.cjs +82 -0
- package/dist/chunk-HW23IB3R.cjs +59 -0
- package/dist/chunk-N3OMUVHL.cjs +33 -0
- package/dist/chunk-NJEVZBJ7.cjs +67 -0
- package/dist/chunk-QHPFQN2C.js +82 -0
- package/dist/chunk-VKPJWS2D.js +59 -0
- package/dist/chunk-WTNWBHMC.js +255 -0
- package/dist/chunk-WZ4RNT3A.js +67 -0
- package/dist/chunk-YWA2XDPM.cjs +255 -0
- package/dist/index.cjs +32 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +32 -0
- package/dist/next.cjs +6 -0
- package/dist/next.d.cts +25 -0
- package/dist/next.d.ts +25 -0
- package/dist/next.js +6 -0
- package/dist/react.cjs +6 -0
- package/dist/react.d.cts +21 -0
- package/dist/react.d.ts +21 -0
- package/dist/react.js +6 -0
- package/dist/types-Q62lBJZ-.d.cts +25 -0
- package/dist/types-Q62lBJZ-.d.ts +25 -0
- package/package.json +71 -0
- package/src/adapters/index.ts +2 -0
- package/src/adapters/postgres.ts +60 -0
- package/src/adapters/sqlite.ts +82 -0
- package/src/components/NotificationBell.tsx +292 -0
- package/src/components/index.ts +2 -0
- package/src/di.ts +19 -0
- package/src/handlers/route.ts +52 -0
- package/src/index.ts +8 -0
- package/src/notify.ts +7 -0
- package/src/types.ts +22 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +16 -0
package/src/di.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NotificationAdapter } from './types'
|
|
2
|
+
|
|
3
|
+
export const NOTIFICATION_ADAPTER = Symbol.for('@cyguin/notify/NotificationAdapter')
|
|
4
|
+
|
|
5
|
+
let _adapter: NotificationAdapter | null = null
|
|
6
|
+
|
|
7
|
+
export function setNotificationAdapter(adapter: NotificationAdapter): void {
|
|
8
|
+
_adapter = adapter
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getNotificationAdapter(): NotificationAdapter {
|
|
12
|
+
if (!_adapter) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
'[@cyguin/notify] Notification adapter not set. ' +
|
|
15
|
+
'Call setNotificationAdapter() before using notify().'
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
return _adapter
|
|
19
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { getNotificationAdapter } from '../di'
|
|
3
|
+
import type { NotifyOptions } from '../types'
|
|
4
|
+
|
|
5
|
+
export interface NotifyHandlerOptions {
|
|
6
|
+
secret?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createNotifyHandler(_options?: NotifyHandlerOptions) {
|
|
10
|
+
const adapter = getNotificationAdapter()
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
async GET(request: Request) {
|
|
14
|
+
const url = new URL(request.url)
|
|
15
|
+
const userId = url.searchParams.get('userId')
|
|
16
|
+
if (!userId) {
|
|
17
|
+
return NextResponse.json({ error: 'userId is required' }, { status: 400 })
|
|
18
|
+
}
|
|
19
|
+
const limit = Number(url.searchParams.get('limit') ?? 20)
|
|
20
|
+
const offset = Number(url.searchParams.get('offset') ?? 0)
|
|
21
|
+
const notifications = await adapter.findManyByUser(userId, { limit, offset })
|
|
22
|
+
return NextResponse.json({ notifications })
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
async POST(request: Request) {
|
|
26
|
+
let body: { userId: string; title: string; body: string; href?: string }
|
|
27
|
+
try {
|
|
28
|
+
body = await request.json()
|
|
29
|
+
} catch {
|
|
30
|
+
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
|
|
31
|
+
}
|
|
32
|
+
const { userId, title, body: notificationBody, href } = body
|
|
33
|
+
if (!userId || !title || !notificationBody) {
|
|
34
|
+
return NextResponse.json({ error: 'userId, title, and body are required' }, { status: 400 })
|
|
35
|
+
}
|
|
36
|
+
const options: NotifyOptions = { title, body: notificationBody, href }
|
|
37
|
+
const notification = await adapter.create({ userId, ...options })
|
|
38
|
+
return NextResponse.json({ notification }, { status: 201 })
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async PATCH(request: Request) {
|
|
42
|
+
const url = new URL(request.url)
|
|
43
|
+
const id = url.searchParams.get('id')
|
|
44
|
+
const userId = url.searchParams.get('userId')
|
|
45
|
+
if (!id || !userId) {
|
|
46
|
+
return NextResponse.json({ error: 'id and userId are required' }, { status: 400 })
|
|
47
|
+
}
|
|
48
|
+
await adapter.markRead(id, userId)
|
|
49
|
+
return NextResponse.json({ ok: true })
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { notify } from './notify'
|
|
2
|
+
export { getNotificationAdapter, setNotificationAdapter, NOTIFICATION_ADAPTER } from './di'
|
|
3
|
+
export { createNotifyHandler } from './handlers/route'
|
|
4
|
+
export type { NotificationAdapter, NotificationRecord, NotifyOptions } from './types'
|
|
5
|
+
export { createSQLiteAdapter, createPostgresAdapter } from './adapters'
|
|
6
|
+
|
|
7
|
+
export { NotificationBell } from './components'
|
|
8
|
+
export type { Notification, NotificationBellProps } from './components'
|
package/src/notify.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getNotificationAdapter } from './di'
|
|
2
|
+
import type { NotifyOptions } from './types'
|
|
3
|
+
|
|
4
|
+
export async function notify(userId: string, options: NotifyOptions): Promise<void> {
|
|
5
|
+
const adapter = getNotificationAdapter()
|
|
6
|
+
await adapter.create({ userId, ...options })
|
|
7
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface NotificationRecord {
|
|
2
|
+
id: string
|
|
3
|
+
userId: string
|
|
4
|
+
title: string
|
|
5
|
+
body: string
|
|
6
|
+
href?: string
|
|
7
|
+
readAt?: number
|
|
8
|
+
createdAt: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface NotifyOptions {
|
|
12
|
+
title: string
|
|
13
|
+
body: string
|
|
14
|
+
href?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface NotificationAdapter {
|
|
18
|
+
create(notification: Omit<NotificationRecord, 'id' | 'createdAt'>): Promise<NotificationRecord>
|
|
19
|
+
findManyByUser(userId: string, options?: { limit?: number; offset?: number }): Promise<NotificationRecord[]>
|
|
20
|
+
markRead(id: string, userId: string): Promise<void>
|
|
21
|
+
countUnread(userId: string): Promise<number>
|
|
22
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"rootDir": "./src",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"noEmit": false,
|
|
18
|
+
"jsx": "react-jsx"
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: {
|
|
5
|
+
index: 'src/index.ts',
|
|
6
|
+
next: 'src/handlers/route.ts',
|
|
7
|
+
react: 'src/components/index.ts',
|
|
8
|
+
'adapters/sqlite': 'src/adapters/sqlite.ts',
|
|
9
|
+
'adapters/postgres': 'src/adapters/postgres.ts',
|
|
10
|
+
},
|
|
11
|
+
format: ['esm', 'cjs'],
|
|
12
|
+
dts: true,
|
|
13
|
+
splitting: true,
|
|
14
|
+
sourcemap: false,
|
|
15
|
+
clean: true,
|
|
16
|
+
});
|