Skip to content

Commit 4a93dcd

Browse files
committed
fix(docs): update README for React Native installation and clarify peer dependencies; feat: implement safe imports for React Native compatibility
1 parent b7e9118 commit 4a93dcd

File tree

6 files changed

+144
-12
lines changed

6 files changed

+144
-12
lines changed

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@
88

99
## Install
1010

11+
### Web / Node.js / Next.js
12+
1113
```bash
1214
npm install @sqlitecloud/drivers
1315
```
1416

15-
## React Native / Expo Install
17+
No additional dependencies required - the package works out of the box.
18+
19+
### React Native / Expo
1620

17-
You also have to install Peer Dependencies
21+
When using this package in React Native or Expo projects, you must install the required peer dependencies. These dependencies are optional and not installed automatically to avoid polluting web and Node.js projects with unnecessary packages.
1822

1923
```bash
20-
npm install @sqlitecloud/drivers react-native-tcp-socket react-native-quick-base64
24+
npm install @sqlitecloud/drivers
25+
npm install react-native-tcp-socket react-native-quick-base64 @craftzdog/react-native-buffer react-native-url-polyfill
2126
```
2227

28+
If you forget to install these dependencies, the package will throw clear error messages indicating which dependency is missing and how to install it.
29+
2330
React Native run iOS
2431

2532
```bash

package-lock.json

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

src/drivers/connection-tls.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@ import {
1717
} from './protocol'
1818
import { type ErrorCallback, type ResultsCallback, SQLiteCloudCommand, type SQLiteCloudConfig, SQLiteCloudError } from './types'
1919
import { getInitializationCommands } from './utilities'
20+
import { getSafeBuffer, getSafeTLS } from './safe-imports'
21+
import type * as TLSTypes from 'tls'
2022

2123
// explicitly importing buffer library to allow cross-platform support by replacing it
22-
import { Buffer } from 'buffer'
24+
// In React Native: Metro resolves 'buffer' to '@craftzdog/react-native-buffer' via package.json react-native field
25+
// In Web/Node: Uses standard buffer package
26+
const Buffer = getSafeBuffer()
2327

24-
import * as tls from 'tls'
28+
// In React Native: Metro resolves 'tls' to 'react-native-tcp-socket' via package.json react-native field
29+
// In Node: Uses native tls module
30+
// In Browser: Returns null (browser field sets tls to false)
31+
const tls = getSafeTLS()
2532

2633
/**
2734
* Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs
2835
* that connect to native sockets or tls sockets and communicates via raw, binary protocol.
2936
*/
3037
export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
3138
/** Currently opened bun socket used to communicated with SQLiteCloud server */
32-
private socket?: tls.TLSSocket
39+
private socket?: TLSTypes.TLSSocket
3340

3441
/** True if connection is open */
3542
get connected(): boolean {
@@ -39,6 +46,20 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
3946
/* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */
4047
connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this {
4148
console.assert(!this.connected, 'SQLiteCloudTlsConnection.connect - connection already established')
49+
50+
// Check if tls is available (it's null in browser contexts)
51+
if (!tls) {
52+
const error = new SQLiteCloudError(
53+
'TLS connections are not available in this environment. Use WebSocket connections instead by setting usewebsocket: true in your configuration.',
54+
{ errorCode: 'ERR_TLS_NOT_AVAILABLE' }
55+
)
56+
if (callback) {
57+
callback.call(this, error)
58+
return this
59+
}
60+
throw error
61+
}
62+
4263
if (this.config.verbose) {
4364
console.debug(`-> connecting ${config?.host as string}:${config?.port as number}`)
4465
}
@@ -84,11 +105,11 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
84105
// https://brooker.co.za/blog/2024/05/09/nagle.html
85106
this.socket.setNoDelay(true)
86107

87-
this.socket.on('data', data => {
108+
this.socket.on('data', (data: Buffer) => {
88109
this.processCommandsData(data)
89110
})
90111

91-
this.socket.on('error', error => {
112+
this.socket.on('error', (error: Error) => {
92113
this.close()
93114
this.processCommandsFinish(new SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }))
94115
})

src/drivers/protocol.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
import { SQLiteCloudRowset } from './rowset'
66
import { SAFE_INTEGER_MODE, SQLiteCloudCommand, SQLiteCloudError, type SQLCloudRowsetMetadata, type SQLiteCloudDataTypes } from './types'
7+
import { getSafeBuffer } from './safe-imports'
78

89
// explicitly importing buffer library to allow cross-platform support by replacing it
9-
import { Buffer } from 'buffer'
10+
// In React Native: Metro resolves 'buffer' to '@craftzdog/react-native-buffer' via package.json react-native field
11+
// In Web/Node: Uses standard buffer package
12+
const Buffer = getSafeBuffer()
1013

1114
// https://www.npmjs.com/package/lz4js
1215
const lz4 = require('lz4js')

src/drivers/safe-imports.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* safe-imports.ts - Safe imports for optional React Native dependencies
3+
*
4+
* This module provides safe imports for dependencies that are optional peer dependencies.
5+
* When these dependencies are not installed (e.g., using the package in a web/Next.js context),
6+
* the imports will still work. However, in React Native contexts where these dependencies are
7+
* required but not installed, clear error messages will be thrown.
8+
*/
9+
10+
import { SQLiteCloudError } from './types'
11+
12+
/**
13+
* Detects if we're running in React Native environment
14+
*/
15+
export function isReactNative(): boolean {
16+
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative'
17+
}
18+
19+
/**
20+
* Safely imports the URL class from whatwg-url or react-native-url-polyfill
21+
* In React Native: Uses react-native-url-polyfill (via react-native field mapping)
22+
* In Web/Node: Uses whatwg-url
23+
*/
24+
export function getSafeURL(): typeof import('whatwg-url').URL {
25+
try {
26+
// In React Native, Metro bundler will resolve this to react-native-url-polyfill
27+
// In Web/Node, this will resolve to whatwg-url
28+
const { URL } = require('whatwg-url')
29+
return URL
30+
} catch (error) {
31+
if (isReactNative()) {
32+
throw new SQLiteCloudError(
33+
'Missing required React Native dependency: react-native-url-polyfill. ' +
34+
'Please install it using: npm install react-native-url-polyfill',
35+
{ errorCode: 'ERR_MISSING_DEPENDENCY', cause: error as Error }
36+
)
37+
}
38+
throw new SQLiteCloudError(
39+
'Failed to load URL parser. Please ensure whatwg-url is installed.',
40+
{ errorCode: 'ERR_MISSING_DEPENDENCY', cause: error as Error }
41+
)
42+
}
43+
}
44+
45+
/**
46+
* Safely imports the Buffer class from buffer or @craftzdog/react-native-buffer
47+
* In React Native: Uses @craftzdog/react-native-buffer (via react-native field mapping)
48+
* In Web/Node: Uses buffer package
49+
*/
50+
export function getSafeBuffer(): typeof import('buffer').Buffer {
51+
try {
52+
// In React Native, Metro bundler will resolve this to @craftzdog/react-native-buffer
53+
// In Web/Node, this will resolve to buffer package
54+
const { Buffer } = require('buffer')
55+
return Buffer
56+
} catch (error) {
57+
if (isReactNative()) {
58+
throw new SQLiteCloudError(
59+
'Missing required React Native dependency: @craftzdog/react-native-buffer. ' +
60+
'Please install it using: npm install @craftzdog/react-native-buffer',
61+
{ errorCode: 'ERR_MISSING_DEPENDENCY', cause: error as Error }
62+
)
63+
}
64+
throw new SQLiteCloudError(
65+
'Failed to load Buffer library. Please ensure buffer package is installed.',
66+
{ errorCode: 'ERR_MISSING_DEPENDENCY', cause: error as Error }
67+
)
68+
}
69+
}
70+
71+
/**
72+
* Safely imports the tls module or react-native-tcp-socket
73+
* In React Native: Uses react-native-tcp-socket (via react-native field mapping)
74+
* In Node: Uses native tls module
75+
* In Browser: Will return null (browser field sets tls to false)
76+
*/
77+
export function getSafeTLS(): typeof import('tls') | null {
78+
try {
79+
// In React Native, Metro bundler will resolve this to react-native-tcp-socket
80+
// In Node, this will resolve to native tls module
81+
// In Browser, the browser field in package.json sets tls to false
82+
const tls = require('tls')
83+
if (tls === false || !tls) {
84+
return null
85+
}
86+
return tls
87+
} catch (error) {
88+
if (isReactNative()) {
89+
throw new SQLiteCloudError(
90+
'Missing required React Native dependency: react-native-tcp-socket. ' +
91+
'Please install it using: npm install react-native-tcp-socket',
92+
{ errorCode: 'ERR_MISSING_DEPENDENCY', cause: error as Error }
93+
)
94+
}
95+
// In browser context, tls is not available (WebSocket should be used instead)
96+
return null
97+
}
98+
}

src/drivers/utilities.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
//
44

55
import { DEFAULT_PORT, DEFAULT_TIMEOUT, SQLiteCloudArrayType, SQLiteCloudConfig, SQLiteCloudDataTypes, SQLiteCloudError } from './types'
6+
import { getSafeURL } from './safe-imports'
67

78
// explicitly importing these libraries to allow cross-platform support by replacing them
8-
import { URL } from 'whatwg-url'
9+
// In React Native: Metro resolves 'whatwg-url' to 'react-native-url-polyfill' via package.json react-native field
10+
// In Web/Node: Uses standard whatwg-url package
11+
const URL = getSafeURL()
912

1013
//
1114
// determining running environment, thanks to browser-or-node

0 commit comments

Comments
 (0)