@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/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
- // objectFit: 'cover',
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
- // backgroundColor: 'white',
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
- 'use client'
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
- import dynamic from 'next/dynamic'
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
- import { useBasic, useQuery } from "@basictech/react"
6
- import LoginButton from "./componets";
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
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from 'tsup'
2
2
 
3
3
  export default defineConfig({
4
- entry: ['src/index.ts'],
4
+ entry: ['src/index.ts', 'src/client.ts'],
5
5
  format: ['cjs', 'esm'],
6
6
  dts: true,
7
7
  splitting: false,
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
- }