@basictech/nextjs 0.6.0-beta.5 → 0.6.0-beta.7
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/.turbo/turbo-build.log +17 -11
- package/CHANGELOG.md +19 -0
- package/dist/client.d.mts +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +46 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +24 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +81 -8
- package/dist/index.d.ts +81 -8
- package/dist/index.js +69 -92
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +65 -81
- package/dist/index.mjs.map +1 -1
- package/package.json +24 -3
- package/readme.md +623 -53
- package/src/client.ts +33 -0
- package/src/{componets.tsx → components.tsx} +8 -8
- package/src/index.ts +35 -11
- package/src/middleware.ts +186 -0
- package/tsup.config.ts +1 -1
- package/src/sync.ts +0 -75
package/src/client.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// @basictech/nextjs/client - Client-side exports for Next.js
|
|
4
|
+
// Use this in client components (files with 'use client' directive)
|
|
5
|
+
|
|
6
|
+
// Re-export everything from @basictech/react for client-side use
|
|
7
|
+
export {
|
|
8
|
+
useBasic,
|
|
9
|
+
BasicProvider,
|
|
10
|
+
useQuery,
|
|
11
|
+
RemoteDB,
|
|
12
|
+
RemoteCollection,
|
|
13
|
+
RemoteDBError,
|
|
14
|
+
NotAuthenticatedError,
|
|
15
|
+
STORAGE_KEYS
|
|
16
|
+
} from "@basictech/react"
|
|
17
|
+
|
|
18
|
+
// Re-export types (DBMode is also a type)
|
|
19
|
+
export type {
|
|
20
|
+
AuthConfig,
|
|
21
|
+
BasicStorage,
|
|
22
|
+
LocalStorageAdapter,
|
|
23
|
+
BasicProviderProps,
|
|
24
|
+
BasicContextType,
|
|
25
|
+
AuthResult,
|
|
26
|
+
BasicDB,
|
|
27
|
+
Collection,
|
|
28
|
+
RemoteDBConfig,
|
|
29
|
+
AuthError
|
|
30
|
+
} from "@basictech/react"
|
|
31
|
+
|
|
32
|
+
// DBMode is a type-only export
|
|
33
|
+
export type { DBMode } from "@basictech/react"
|
|
@@ -32,7 +32,7 @@ const LoginButton = () => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// Basic styles
|
|
35
|
-
const avatarContainerStyle = {
|
|
35
|
+
const avatarContainerStyle: React.CSSProperties = {
|
|
36
36
|
display: 'inline-flex',
|
|
37
37
|
alignItems: 'center',
|
|
38
38
|
justifyContent: 'center',
|
|
@@ -43,14 +43,14 @@ const avatarContainerStyle = {
|
|
|
43
43
|
cursor: 'pointer',
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const avatarImageStyle = {
|
|
46
|
+
const avatarImageStyle: React.CSSProperties = {
|
|
47
47
|
width: '100%',
|
|
48
48
|
height: '100%',
|
|
49
49
|
borderRadius: '100%',
|
|
50
|
-
|
|
50
|
+
objectFit: 'cover',
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
const avatarFallbackStyle = {
|
|
53
|
+
const avatarFallbackStyle: React.CSSProperties = {
|
|
54
54
|
width: '100%',
|
|
55
55
|
height: '100%',
|
|
56
56
|
borderRadius: '100%',
|
|
@@ -62,14 +62,14 @@ const avatarFallbackStyle = {
|
|
|
62
62
|
fontSize: '20px',
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
const popoverContentStyle = {
|
|
65
|
+
const popoverContentStyle: React.CSSProperties = {
|
|
66
66
|
padding: '20px',
|
|
67
67
|
borderRadius: '8px',
|
|
68
|
-
|
|
68
|
+
backgroundColor: 'white',
|
|
69
69
|
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.2)',
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
const buttonStyle = {
|
|
72
|
+
const buttonStyle: React.CSSProperties = {
|
|
73
73
|
padding: '8px 16px',
|
|
74
74
|
backgroundColor: '#007bff',
|
|
75
75
|
color: '#fff',
|
|
@@ -78,7 +78,7 @@ const buttonStyle = {
|
|
|
78
78
|
cursor: 'pointer',
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
const popoverArrowStyle = {
|
|
81
|
+
const popoverArrowStyle: React.CSSProperties = {
|
|
82
82
|
fill: 'white',
|
|
83
83
|
};
|
|
84
84
|
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
// @basictech/nextjs - Next.js integration for Basic SDK
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// - Server components/middleware: import from "@basictech/nextjs"
|
|
5
|
+
// - Client components: import from "@basictech/nextjs/client"
|
|
6
|
+
//
|
|
7
|
+
// Example:
|
|
8
|
+
// // In a client component (providers.tsx)
|
|
9
|
+
// 'use client'
|
|
10
|
+
// import { BasicProvider, useBasic } from "@basictech/nextjs/client"
|
|
11
|
+
//
|
|
12
|
+
// // In middleware.ts
|
|
13
|
+
// import { createBasicMiddleware } from "@basictech/nextjs"
|
|
2
14
|
|
|
3
|
-
|
|
15
|
+
// Re-export types from react package (types are safe for SSR)
|
|
16
|
+
export type {
|
|
17
|
+
AuthConfig,
|
|
18
|
+
BasicStorage,
|
|
19
|
+
LocalStorageAdapter,
|
|
20
|
+
BasicProviderProps,
|
|
21
|
+
BasicContextType,
|
|
22
|
+
AuthResult,
|
|
23
|
+
DBMode,
|
|
24
|
+
BasicDB,
|
|
25
|
+
Collection,
|
|
26
|
+
RemoteDBConfig,
|
|
27
|
+
AuthError
|
|
28
|
+
} from "@basictech/react"
|
|
4
29
|
|
|
5
|
-
|
|
6
|
-
|
|
30
|
+
// Middleware exports (server-side safe - no dexie dependency)
|
|
31
|
+
export {
|
|
32
|
+
createBasicMiddleware,
|
|
33
|
+
withBasicAuth,
|
|
34
|
+
getAuthFromRequest,
|
|
35
|
+
getReturnUrl
|
|
36
|
+
} from "./middleware"
|
|
7
37
|
|
|
8
|
-
|
|
9
|
-
const BasicProvider = dynamic(() => import('@basictech/react').then(mod => mod.BasicProvider), { ssr: false });
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
useBasic, BasicProvider, useQuery, LoginButton
|
|
14
|
-
}
|
|
38
|
+
export type { BasicMiddlewareConfig } from "./middleware"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for the Basic auth middleware
|
|
5
|
+
*/
|
|
6
|
+
export interface BasicMiddlewareConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Routes that require authentication
|
|
9
|
+
* Supports glob patterns like '/dashboard/*', '/api/protected/*'
|
|
10
|
+
*/
|
|
11
|
+
protectedRoutes?: string[]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Routes that are always public (bypass auth check)
|
|
15
|
+
* Useful for login pages, public APIs, etc.
|
|
16
|
+
*/
|
|
17
|
+
publicRoutes?: string[]
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Where to redirect unauthenticated users
|
|
21
|
+
* @default '/login'
|
|
22
|
+
*/
|
|
23
|
+
signInUrl?: string
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Where to redirect after successful sign-in
|
|
27
|
+
* @default '/'
|
|
28
|
+
*/
|
|
29
|
+
afterSignInUrl?: string
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cookie name for the access token
|
|
33
|
+
* @default 'basic_access_token'
|
|
34
|
+
*/
|
|
35
|
+
tokenCookieName?: string
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Cookie name for the full token object
|
|
39
|
+
* @default 'basic_token'
|
|
40
|
+
*/
|
|
41
|
+
fullTokenCookieName?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_CONFIG: Required<BasicMiddlewareConfig> = {
|
|
45
|
+
protectedRoutes: [],
|
|
46
|
+
publicRoutes: ['/login', '/signup', '/auth/*'],
|
|
47
|
+
signInUrl: '/login',
|
|
48
|
+
afterSignInUrl: '/',
|
|
49
|
+
tokenCookieName: 'basic_access_token',
|
|
50
|
+
fullTokenCookieName: 'basic_token'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if a path matches any of the patterns
|
|
55
|
+
* Supports simple glob patterns with * wildcard
|
|
56
|
+
*/
|
|
57
|
+
function matchesPattern(path: string, patterns: string[]): boolean {
|
|
58
|
+
return patterns.some(pattern => {
|
|
59
|
+
// Convert glob pattern to regex
|
|
60
|
+
const regexPattern = pattern
|
|
61
|
+
.replace(/\*/g, '.*') // Replace * with .*
|
|
62
|
+
.replace(/\//g, '\\/') // Escape slashes
|
|
63
|
+
const regex = new RegExp(`^${regexPattern}$`)
|
|
64
|
+
return regex.test(path)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if the user has a valid token
|
|
70
|
+
*/
|
|
71
|
+
function hasValidToken(request: NextRequest, config: Required<BasicMiddlewareConfig>): boolean {
|
|
72
|
+
const token = request.cookies.get(config.tokenCookieName)?.value
|
|
73
|
+
|
|
74
|
+
if (!token) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Basic validation - token exists and is not empty
|
|
79
|
+
// For more thorough validation, you'd decode the JWT and check expiry
|
|
80
|
+
// But that adds latency to every request
|
|
81
|
+
return token.length > 0
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get auth info from cookies
|
|
86
|
+
*/
|
|
87
|
+
export function getAuthFromRequest(request: NextRequest, config?: Partial<BasicMiddlewareConfig>): {
|
|
88
|
+
isAuthenticated: boolean
|
|
89
|
+
token: string | null
|
|
90
|
+
} {
|
|
91
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config }
|
|
92
|
+
const token = request.cookies.get(mergedConfig.tokenCookieName)?.value || null
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
isAuthenticated: !!token && token.length > 0,
|
|
96
|
+
token
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a Basic auth middleware for NextJS
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // In middleware.ts at the root of your NextJS app:
|
|
105
|
+
* import { createBasicMiddleware } from '@basictech/nextjs'
|
|
106
|
+
*
|
|
107
|
+
* export const middleware = createBasicMiddleware({
|
|
108
|
+
* protectedRoutes: ['/dashboard/*', '/settings/*'],
|
|
109
|
+
* publicRoutes: ['/login', '/signup', '/'],
|
|
110
|
+
* signInUrl: '/login'
|
|
111
|
+
* })
|
|
112
|
+
*
|
|
113
|
+
* export const config = {
|
|
114
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']
|
|
115
|
+
* }
|
|
116
|
+
*/
|
|
117
|
+
export function createBasicMiddleware(config?: BasicMiddlewareConfig) {
|
|
118
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config }
|
|
119
|
+
|
|
120
|
+
return function middleware(request: NextRequest): NextResponse {
|
|
121
|
+
const { pathname } = request.nextUrl
|
|
122
|
+
|
|
123
|
+
// Skip middleware for static files and Next.js internals
|
|
124
|
+
if (
|
|
125
|
+
pathname.startsWith('/_next') ||
|
|
126
|
+
pathname.startsWith('/api/_next') ||
|
|
127
|
+
pathname.includes('.') // Static files like .css, .js, .ico
|
|
128
|
+
) {
|
|
129
|
+
return NextResponse.next()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check if route is explicitly public
|
|
133
|
+
if (matchesPattern(pathname, mergedConfig.publicRoutes)) {
|
|
134
|
+
return NextResponse.next()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check if route is protected
|
|
138
|
+
const isProtectedRoute = mergedConfig.protectedRoutes.length === 0
|
|
139
|
+
? true // If no protected routes specified, protect everything except public
|
|
140
|
+
: matchesPattern(pathname, mergedConfig.protectedRoutes)
|
|
141
|
+
|
|
142
|
+
if (!isProtectedRoute) {
|
|
143
|
+
return NextResponse.next()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check authentication
|
|
147
|
+
const isAuthenticated = hasValidToken(request, mergedConfig)
|
|
148
|
+
|
|
149
|
+
if (!isAuthenticated) {
|
|
150
|
+
// Redirect to sign-in page with return URL
|
|
151
|
+
const signInUrl = new URL(mergedConfig.signInUrl, request.url)
|
|
152
|
+
signInUrl.searchParams.set('returnUrl', pathname)
|
|
153
|
+
|
|
154
|
+
return NextResponse.redirect(signInUrl)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// User is authenticated, allow the request
|
|
158
|
+
return NextResponse.next()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Simple auth check middleware - redirects unauthenticated users
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // In middleware.ts
|
|
167
|
+
* import { withBasicAuth } from '@basictech/nextjs'
|
|
168
|
+
*
|
|
169
|
+
* export const middleware = withBasicAuth
|
|
170
|
+
*
|
|
171
|
+
* export const config = {
|
|
172
|
+
* matcher: ['/dashboard/:path*', '/settings/:path*']
|
|
173
|
+
* }
|
|
174
|
+
*/
|
|
175
|
+
export function withBasicAuth(request: NextRequest): NextResponse {
|
|
176
|
+
return createBasicMiddleware()(request)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Helper to get the return URL from search params
|
|
181
|
+
*/
|
|
182
|
+
export function getReturnUrl(request: NextRequest, defaultUrl: string = '/'): string {
|
|
183
|
+
const returnUrl = request.nextUrl.searchParams.get('returnUrl')
|
|
184
|
+
return returnUrl || defaultUrl
|
|
185
|
+
}
|
|
186
|
+
|
package/tsup.config.ts
CHANGED
package/src/sync.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
|
|
2
|
-
|
|
3
|
-
export function sync() {
|
|
4
|
-
console.log("sync");
|
|
5
|
-
|
|
6
|
-
async function storeFileToOPFS(fileName: string, fileContent: string | ArrayBuffer) {
|
|
7
|
-
if (!('storage' in navigator && 'getDirectory' in navigator.storage)) {
|
|
8
|
-
console.error('OPFS is not supported in this browser');
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const root = await navigator.storage.getDirectory();
|
|
14
|
-
|
|
15
|
-
// Create or open a file
|
|
16
|
-
const fileHandle = await root.getFileHandle(fileName, { create: true });
|
|
17
|
-
|
|
18
|
-
const writable = await fileHandle.createWritable();
|
|
19
|
-
|
|
20
|
-
await writable.write(fileContent);
|
|
21
|
-
|
|
22
|
-
await writable.close();
|
|
23
|
-
|
|
24
|
-
console.log(`File "${fileName}" has been stored in OPFS`);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error('Error storing file to OPFS:', error);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// storeFileToOPFS(fileName, fileContent);
|
|
32
|
-
|
|
33
|
-
const log = console.log;
|
|
34
|
-
const error = console.error;
|
|
35
|
-
|
|
36
|
-
// const start = (sqlite3 : any) => {
|
|
37
|
-
// log('Running SQLite3 version', sqlite3.version.libVersion);
|
|
38
|
-
// const db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct');
|
|
39
|
-
|
|
40
|
-
// const result = db.exec('SELECT * FROM sqlite_master');
|
|
41
|
-
// // Your SQLite code here.
|
|
42
|
-
// };
|
|
43
|
-
|
|
44
|
-
const initializeSQLite = async () => {
|
|
45
|
-
try {
|
|
46
|
-
log('Loading and initializing SQLite3 module...');
|
|
47
|
-
const sqlite3 = await sqlite3InitModule({
|
|
48
|
-
print: log,
|
|
49
|
-
printErr: error,
|
|
50
|
-
});
|
|
51
|
-
log('Done initializing. Running demo...');
|
|
52
|
-
log(sqlite3.version.libVersion);
|
|
53
|
-
|
|
54
|
-
const db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct');
|
|
55
|
-
const result = db.exec('SELECT * FROM sqlite_master');
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
log(result);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// start(sqlite3);
|
|
63
|
-
} catch (err: any) {
|
|
64
|
-
|
|
65
|
-
error('Initialization error:', err.name, err.message);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
initializeSQLite();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|