@unvired/react-native-unvired-sdk 0.0.9 → 0.0.10
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/BuildNo.txt +1 -1
- package/README.md +18 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/logger/BaseLogger.d.ts +15 -5
- package/dist/logger/BaseLogger.js +48 -4
- package/dist/logger/Logger.d.ts +2 -1
- package/dist/logger/Logger.js +5 -0
- package/dist/logger/Logger.types.d.ts +71 -0
- package/dist/logger/Logger.types.js +3 -0
- package/dist/logger/{LoggerNative.d.ts → services/LoggerNative.d.ts} +5 -3
- package/dist/logger/services/LoggerNative.js +212 -0
- package/dist/logger/services/LoggerWeb.d.ts +10 -0
- package/dist/logger/{LoggerWeb.js → services/LoggerWeb.js} +5 -3
- package/package.json +6 -13
- package/dist/logger/LoggerNative.js +0 -84
- package/dist/logger/LoggerWeb.d.ts +0 -8
- package/dist/logger/LoggerWindows.d.ts +0 -10
- package/dist/logger/LoggerWindows.js +0 -44
- package/example/PlatformInterface.ts +0 -11
- package/example/ReactNativePlatformAdapter.ts +0 -40
package/BuildNo.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
R-0.000.
|
|
1
|
+
R-0.000.0010
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The **logger works out of the box** with zero dependencies. For additional featu
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# For file operations
|
|
19
|
-
npm install react-native-fs
|
|
19
|
+
npm install @dr.pogodin/react-native-fs
|
|
20
20
|
|
|
21
21
|
# For database (choose one)
|
|
22
22
|
npm install react-native-sqlite-storage # SQL database
|
|
@@ -28,6 +28,23 @@ npm install @notifee/react-native
|
|
|
28
28
|
|
|
29
29
|
> **See [NPM_PACKAGES.md](./NPM_PACKAGES.md) for detailed package information and implementation guide.**
|
|
30
30
|
|
|
31
|
+
### Troubleshooting
|
|
32
|
+
|
|
33
|
+
If you encounter errors like:
|
|
34
|
+
- `Cannot read property 'RNFSFileTypeRegular' of null`
|
|
35
|
+
- `Cannot read property 'PlatformManager' of undefined`
|
|
36
|
+
- Native module linking issues
|
|
37
|
+
|
|
38
|
+
**See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for detailed solutions.**
|
|
39
|
+
|
|
40
|
+
The SDK now includes defensive checks and will gracefully handle missing native modules with helpful error messages in the console.
|
|
41
|
+
|
|
42
|
+
### Migration from react-native-fs
|
|
43
|
+
|
|
44
|
+
> **Note:** We now use `@dr.pogodin/react-native-fs` (actively maintained fork) instead of the deprecated `react-native-fs`.
|
|
45
|
+
>
|
|
46
|
+
> If you're upgrading from an older version, see [MIGRATION_GUIDE.md](./MIGRATION_GUIDE.md) for step-by-step instructions.
|
|
47
|
+
|
|
31
48
|
## Features
|
|
32
49
|
|
|
33
50
|
- 📝 **Logger** - Comprehensive logging with multiple log levels ✅ **No dependencies**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { Logger,
|
|
1
|
+
export { Logger, logger } from './logger/Logger';
|
|
2
|
+
export type { LogLevel, ILogger, ILoggerImplementation, ILoggerConfig, ILogEntry, ILogFileInfo } from './logger/Logger';
|
|
2
3
|
export { default } from './logger/Logger';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { LogLevel } from './Logger.types';
|
|
2
|
+
export type { LogLevel };
|
|
2
3
|
export declare const MAX_LOG_SIZE: number;
|
|
3
4
|
export declare class BaseLogger {
|
|
4
5
|
private impl;
|
|
@@ -11,8 +12,17 @@ export declare class BaseLogger {
|
|
|
11
12
|
logDebug(c: string, m: string, msg: string): void;
|
|
12
13
|
logInfo(c: string, m: string, msg: string): void;
|
|
13
14
|
logError(c: string, m: string, msg: string): void;
|
|
14
|
-
getLogFileURL():
|
|
15
|
-
getLogFileContent():
|
|
16
|
-
getBackupLogFileContent():
|
|
17
|
-
clearLogFile():
|
|
15
|
+
getLogFileURL(): Promise<string>;
|
|
16
|
+
getLogFileContent(): Promise<string>;
|
|
17
|
+
getBackupLogFileContent(): Promise<string | null>;
|
|
18
|
+
clearLogFile(): Promise<void>;
|
|
19
|
+
get logFile(): string;
|
|
20
|
+
write(message: string): Promise<void>;
|
|
21
|
+
readLog(): Promise<string>;
|
|
22
|
+
rotateLog(): Promise<void>;
|
|
23
|
+
getLogSizes(): Promise<{
|
|
24
|
+
logFile: number;
|
|
25
|
+
backupFile: number;
|
|
26
|
+
}>;
|
|
27
|
+
private getFileSize;
|
|
18
28
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// BaseLogger.ts
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
|
-
import { LoggerNative } from './LoggerNative';
|
|
4
|
-
|
|
5
|
-
import { LoggerWeb } from './LoggerWeb';
|
|
3
|
+
import { LoggerNative } from './services/LoggerNative';
|
|
4
|
+
import { LoggerWeb } from './services/LoggerWeb';
|
|
6
5
|
export const MAX_LOG_SIZE = 1 * 1024 * 1024;
|
|
7
6
|
export class BaseLogger {
|
|
8
7
|
constructor() {
|
|
@@ -11,7 +10,9 @@ export class BaseLogger {
|
|
|
11
10
|
this.impl = new LoggerWeb();
|
|
12
11
|
}
|
|
13
12
|
else if (Platform.OS === 'windows') {
|
|
14
|
-
//
|
|
13
|
+
// TODO: Implement LoggerWindows
|
|
14
|
+
console.warn('Windows logger not implemented yet, using web logger');
|
|
15
|
+
this.impl = new LoggerWeb();
|
|
15
16
|
}
|
|
16
17
|
else {
|
|
17
18
|
this.impl = new LoggerNative();
|
|
@@ -71,4 +72,47 @@ export class BaseLogger {
|
|
|
71
72
|
getLogFileContent() { return this.impl.getLogFileContent(); }
|
|
72
73
|
getBackupLogFileContent() { return this.impl.getBackupLogFileContent(); }
|
|
73
74
|
clearLogFile() { return this.impl.clear(); }
|
|
75
|
+
// -------------------------
|
|
76
|
+
// ILogger interface compatibility
|
|
77
|
+
// -------------------------
|
|
78
|
+
get logFile() {
|
|
79
|
+
return this.impl.logFile || '';
|
|
80
|
+
}
|
|
81
|
+
async write(message) {
|
|
82
|
+
const line = `[${new Date().toISOString()}] ${message}`;
|
|
83
|
+
await this.impl.writeLine(line, MAX_LOG_SIZE);
|
|
84
|
+
}
|
|
85
|
+
async readLog() {
|
|
86
|
+
return this.impl.getLogFileContent();
|
|
87
|
+
}
|
|
88
|
+
async rotateLog() {
|
|
89
|
+
if (this.impl.rotateLog) {
|
|
90
|
+
await this.impl.rotateLog();
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// For Windows, force rotation by writing max size
|
|
94
|
+
await this.impl.writeLine('', Number.MAX_SAFE_INTEGER);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async getLogSizes() {
|
|
98
|
+
const logSize = await this.getFileSize(this.impl.logFile);
|
|
99
|
+
const backupSize = await this.getFileSize(this.impl.backupZip || this.impl.tempFile || '');
|
|
100
|
+
return { logFile: logSize, backupFile: backupSize };
|
|
101
|
+
}
|
|
102
|
+
async getFileSize(path) {
|
|
103
|
+
if (!path)
|
|
104
|
+
return 0;
|
|
105
|
+
try {
|
|
106
|
+
// Try to use RNFS if available
|
|
107
|
+
const RNFS = require('@dr.pogodin/react-native-fs');
|
|
108
|
+
if (await RNFS.exists(path)) {
|
|
109
|
+
const stat = await RNFS.stat(path);
|
|
110
|
+
return stat.size;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Ignore
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
74
118
|
}
|
package/dist/logger/Logger.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ import { BaseLogger, LogLevel } from './BaseLogger';
|
|
|
2
2
|
export declare class Logger extends BaseLogger {
|
|
3
3
|
}
|
|
4
4
|
export declare const logger: Logger;
|
|
5
|
-
export {
|
|
5
|
+
export type { ILogger, ILoggerImplementation, ILoggerConfig, ILogEntry, ILogFileInfo, } from './Logger.types';
|
|
6
|
+
export type { LogLevel };
|
|
6
7
|
export default logger;
|
package/dist/logger/Logger.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Logger.ts
|
|
2
2
|
import { BaseLogger } from './BaseLogger';
|
|
3
|
+
// Export the Logger class
|
|
3
4
|
export class Logger extends BaseLogger {
|
|
4
5
|
}
|
|
6
|
+
// Export the singleton logger instance
|
|
5
7
|
export const logger = new Logger();
|
|
6
8
|
// Default export (logger instance)
|
|
7
9
|
export default logger;
|
|
10
|
+
// adb shell
|
|
11
|
+
// run-as com.rnturboapp
|
|
12
|
+
// ls -lh files/localLogs
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log levels supported by the logger
|
|
3
|
+
*/
|
|
4
|
+
export type LogLevel = 'debug' | 'info' | 'error' | 'none';
|
|
5
|
+
/**
|
|
6
|
+
* Interface for platform-specific logger implementations
|
|
7
|
+
* This interface must be implemented by LoggerNative, LoggerWindows, and LoggerWeb
|
|
8
|
+
*/
|
|
9
|
+
export interface ILoggerImplementation {
|
|
10
|
+
logDir?: string;
|
|
11
|
+
logFile: string;
|
|
12
|
+
tempFile?: string;
|
|
13
|
+
backupZip?: string;
|
|
14
|
+
init(): Promise<void>;
|
|
15
|
+
writeLine(line: string, maxSize: number): Promise<void>;
|
|
16
|
+
rotateLog?(): Promise<void>;
|
|
17
|
+
getLogFileURL(): Promise<string>;
|
|
18
|
+
getLogFileContent(): Promise<string>;
|
|
19
|
+
getBackupLogFileContent(): Promise<string | null>;
|
|
20
|
+
clear(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Public API interface for the logger
|
|
24
|
+
* This is what consumers of the logger package should use
|
|
25
|
+
*/
|
|
26
|
+
export interface ILogger {
|
|
27
|
+
logFile: string;
|
|
28
|
+
setLogLevel?(level: LogLevel): void;
|
|
29
|
+
logDebug?(className: string, methodName: string, message: string): void;
|
|
30
|
+
logInfo?(className: string, methodName: string, message: string): void;
|
|
31
|
+
logError?(className: string, methodName: string, message: string): void;
|
|
32
|
+
write(message: string): Promise<void>;
|
|
33
|
+
readLog(): Promise<string>;
|
|
34
|
+
rotateLog(): Promise<void>;
|
|
35
|
+
getLogSizes(): Promise<{
|
|
36
|
+
logFile: number;
|
|
37
|
+
backupFile: number;
|
|
38
|
+
}>;
|
|
39
|
+
getLogFileURL?(): Promise<string>;
|
|
40
|
+
getLogFileContent?(): Promise<string>;
|
|
41
|
+
getBackupLogFileContent(): Promise<string | null>;
|
|
42
|
+
clearLogFile?(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Configuration options for logger initialization
|
|
46
|
+
*/
|
|
47
|
+
export interface ILoggerConfig {
|
|
48
|
+
logLevel?: LogLevel;
|
|
49
|
+
maxLogSize?: number;
|
|
50
|
+
logDir?: string;
|
|
51
|
+
logFileName?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Log entry structure
|
|
55
|
+
*/
|
|
56
|
+
export interface ILogEntry {
|
|
57
|
+
timestamp: string;
|
|
58
|
+
level: LogLevel;
|
|
59
|
+
className?: string;
|
|
60
|
+
methodName?: string;
|
|
61
|
+
message: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Log file information
|
|
65
|
+
*/
|
|
66
|
+
export interface ILogFileInfo {
|
|
67
|
+
path: string;
|
|
68
|
+
size: number;
|
|
69
|
+
exists: boolean;
|
|
70
|
+
lastModified?: Date;
|
|
71
|
+
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ILoggerImplementation } from '../Logger.types';
|
|
2
|
+
export declare class LoggerNative implements ILoggerImplementation {
|
|
2
3
|
logDir: string;
|
|
3
4
|
logFile: string;
|
|
4
|
-
tempFile: string;
|
|
5
5
|
backupZip: string;
|
|
6
|
+
private isRotating;
|
|
7
|
+
private isAvailable;
|
|
6
8
|
constructor();
|
|
7
9
|
init(): Promise<void>;
|
|
8
10
|
writeLine(line: string, maxSize: number): Promise<void>;
|
|
9
11
|
private ensureDir;
|
|
10
12
|
rotateLog(): Promise<void>;
|
|
11
13
|
getLogFileURL(): Promise<string>;
|
|
12
|
-
getLogFileContent(): Promise<
|
|
14
|
+
getLogFileContent(): Promise<any>;
|
|
13
15
|
getBackupLogFileContent(): Promise<string | null>;
|
|
14
16
|
clear(): Promise<void>;
|
|
15
17
|
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// LoggerNative.ts
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
// Import with null safety
|
|
4
|
+
let RNFS = null;
|
|
5
|
+
let zip = null;
|
|
6
|
+
try {
|
|
7
|
+
RNFS = require('@dr.pogodin/react-native-fs');
|
|
8
|
+
if (!RNFS || !RNFS.DocumentDirectoryPath) {
|
|
9
|
+
console.error('❌ @dr.pogodin/react-native-fs is not properly linked. Please run: cd ios && pod install (iOS) or rebuild your Android app.');
|
|
10
|
+
RNFS = null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
console.error('❌ @dr.pogodin/react-native-fs module not found. Please install: npm install @dr.pogodin/react-native-fs');
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const zipModule = require('react-native-zip-archive');
|
|
18
|
+
zip = zipModule.zip;
|
|
19
|
+
if (!zip) {
|
|
20
|
+
console.error('❌ react-native-zip-archive is not properly linked. Please run: cd ios && pod install (iOS) or rebuild your Android app.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.error('❌ react-native-zip-archive module not found. Please install: npm install react-native-zip-archive');
|
|
25
|
+
}
|
|
26
|
+
export class LoggerNative {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.isRotating = false; // Rotation lock to prevent concurrent rotations
|
|
29
|
+
this.isAvailable = false;
|
|
30
|
+
const dir = 'unvired_logs_local';
|
|
31
|
+
const file = 'applog.txt'; // main log file
|
|
32
|
+
if (!RNFS) {
|
|
33
|
+
console.warn('⚠️ LoggerNative: RNFS not available, logging to file system disabled');
|
|
34
|
+
this.logDir = '';
|
|
35
|
+
this.logFile = '';
|
|
36
|
+
this.backupZip = '';
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.isAvailable = true;
|
|
40
|
+
if (Platform.OS === 'android') {
|
|
41
|
+
this.logDir = `${RNFS.DocumentDirectoryPath}/${dir}`;
|
|
42
|
+
}
|
|
43
|
+
else if (Platform.OS === 'ios') {
|
|
44
|
+
this.logDir = `${RNFS.LibraryDirectoryPath}/${dir}`;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.logDir = '';
|
|
48
|
+
}
|
|
49
|
+
this.logFile = `${this.logDir}/${file}`;
|
|
50
|
+
this.backupZip = `${this.logDir}/backuplog.zip`;
|
|
51
|
+
}
|
|
52
|
+
async init() {
|
|
53
|
+
if (!this.isAvailable || !this.logDir || !RNFS)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
if (!(await RNFS.exists(this.logDir)))
|
|
57
|
+
await RNFS.mkdir(this.logDir);
|
|
58
|
+
if (!(await RNFS.exists(this.logFile)))
|
|
59
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
console.error('❌ LoggerNative init error:', e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async writeLine(line, maxSize) {
|
|
66
|
+
if (!this.isAvailable || !RNFS)
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
await this.ensureDir();
|
|
70
|
+
await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
|
|
71
|
+
// Only check for rotation if not already rotating
|
|
72
|
+
if (!this.isRotating) {
|
|
73
|
+
const stat = await RNFS.stat(this.logFile);
|
|
74
|
+
if (Number(stat.size) >= maxSize) {
|
|
75
|
+
// Don't await - let rotation happen in background
|
|
76
|
+
this.rotateLog();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error('❌ LoggerNative writeLine error:', e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async ensureDir() {
|
|
85
|
+
if (!this.isAvailable || !this.logDir || !RNFS)
|
|
86
|
+
return;
|
|
87
|
+
try {
|
|
88
|
+
if (!(await RNFS.exists(this.logDir)))
|
|
89
|
+
await RNFS.mkdir(this.logDir);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
console.error('❌ LoggerNative ensureDir error:', e);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async rotateLog() {
|
|
96
|
+
if (!this.isAvailable || !RNFS || !zip) {
|
|
97
|
+
console.warn('⚠️ LoggerNative: Cannot rotate log, RNFS or zip not available');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Prevent concurrent rotations
|
|
101
|
+
if (this.isRotating) {
|
|
102
|
+
console.log('⏭️ Rotation already in progress, skipping...');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.isRotating = true;
|
|
106
|
+
try {
|
|
107
|
+
console.log('🔄 Starting log rotation...');
|
|
108
|
+
// Check if log file exists and get its size
|
|
109
|
+
if (!(await RNFS.exists(this.logFile))) {
|
|
110
|
+
console.log('⚠️ Log file does not exist, skipping rotation');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const stat = await RNFS.stat(this.logFile);
|
|
114
|
+
console.log(`📊 Current log file size: ${stat.size} bytes`);
|
|
115
|
+
// Step 1: Remove old backup.zip if it exists
|
|
116
|
+
if (await RNFS.exists(this.backupZip)) {
|
|
117
|
+
console.log('🗑️ Removing old backup.zip');
|
|
118
|
+
await RNFS.unlink(this.backupZip);
|
|
119
|
+
}
|
|
120
|
+
// Step 2: Create a temporary directory for zipping
|
|
121
|
+
// react-native-zip-archive requires a directory, not a single file
|
|
122
|
+
const tempZipDir = `${this.logDir}/temp_zip`;
|
|
123
|
+
if (await RNFS.exists(tempZipDir)) {
|
|
124
|
+
await RNFS.unlink(tempZipDir);
|
|
125
|
+
}
|
|
126
|
+
await RNFS.mkdir(tempZipDir);
|
|
127
|
+
// Step 3: Copy current log file into the temp directory
|
|
128
|
+
const tempFileInDir = `${tempZipDir}/applog.txt`;
|
|
129
|
+
console.log('📦 Copying log file to temp directory for zipping');
|
|
130
|
+
await RNFS.copyFile(this.logFile, tempFileInDir);
|
|
131
|
+
// Step 4: Zip the directory → backup.zip
|
|
132
|
+
console.log('🗜️ Creating backup.zip from current log');
|
|
133
|
+
await zip(tempZipDir, this.backupZip);
|
|
134
|
+
const zipStat = await RNFS.stat(this.backupZip);
|
|
135
|
+
console.log(`✅ Backup.zip created successfully (${zipStat.size} bytes)`);
|
|
136
|
+
// Step 5: Clean up temp directory
|
|
137
|
+
console.log('🧹 Cleaning up temp directory');
|
|
138
|
+
if (await RNFS.exists(tempZipDir)) {
|
|
139
|
+
await RNFS.unlink(tempZipDir);
|
|
140
|
+
}
|
|
141
|
+
// Step 6: Clear the current log file (make it fresh/empty)
|
|
142
|
+
console.log('📝 Clearing current log file');
|
|
143
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
144
|
+
console.log('✔ Log rotation completed successfully');
|
|
145
|
+
console.log('📂 Files: applog.txt (fresh) + backuplog.zip (old logs)');
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
console.error('❌ Logger rotation error:', e);
|
|
149
|
+
// Try to recover by creating a fresh log file if it doesn't exist
|
|
150
|
+
try {
|
|
151
|
+
if (!(await RNFS.exists(this.logFile))) {
|
|
152
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
153
|
+
console.log('🔧 Recovery: Created fresh log file');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (recoveryError) {
|
|
157
|
+
console.error('❌ Recovery failed:', recoveryError);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
// Always release the lock
|
|
162
|
+
this.isRotating = false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// ------------------------
|
|
166
|
+
// Public accessors
|
|
167
|
+
// ------------------------
|
|
168
|
+
async getLogFileURL() {
|
|
169
|
+
if (!this.isAvailable)
|
|
170
|
+
return '';
|
|
171
|
+
return this.logFile;
|
|
172
|
+
}
|
|
173
|
+
async getLogFileContent() {
|
|
174
|
+
if (!this.isAvailable || !RNFS)
|
|
175
|
+
return '';
|
|
176
|
+
try {
|
|
177
|
+
return (await RNFS.exists(this.logFile)) ? RNFS.readFile(this.logFile, 'utf8') : '';
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
console.error('❌ LoggerNative getLogFileContent error:', e);
|
|
181
|
+
return '';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async getBackupLogFileContent() {
|
|
185
|
+
if (!this.isAvailable || !RNFS)
|
|
186
|
+
return null;
|
|
187
|
+
try {
|
|
188
|
+
if (await RNFS.exists(this.backupZip)) {
|
|
189
|
+
const stat = await RNFS.stat(this.backupZip);
|
|
190
|
+
return `${stat.size}`;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
console.error('❌ LoggerNative getBackupLogFileContent error:', e);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async clear() {
|
|
200
|
+
if (!this.isAvailable || !RNFS)
|
|
201
|
+
return;
|
|
202
|
+
try {
|
|
203
|
+
if (await RNFS.exists(this.logFile))
|
|
204
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
205
|
+
if (await RNFS.exists(this.backupZip))
|
|
206
|
+
await RNFS.unlink(this.backupZip);
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
console.error('❌ LoggerNative clear error:', e);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ILoggerImplementation } from '../Logger.types';
|
|
2
|
+
export declare class LoggerWeb implements ILoggerImplementation {
|
|
3
|
+
logFile: string;
|
|
4
|
+
init(): Promise<void>;
|
|
5
|
+
writeLine(line: string, maxSize: number): Promise<void>;
|
|
6
|
+
getLogFileURL(): Promise<string>;
|
|
7
|
+
getLogFileContent(): Promise<string>;
|
|
8
|
+
getBackupLogFileContent(): Promise<null>;
|
|
9
|
+
clear(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
// LoggerWeb.ts
|
|
2
1
|
export class LoggerWeb {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.logFile = '';
|
|
4
|
+
}
|
|
3
5
|
async init() { }
|
|
4
|
-
async writeLine(line) {
|
|
6
|
+
async writeLine(line, maxSize) {
|
|
5
7
|
console.log(line);
|
|
6
8
|
}
|
|
7
9
|
async getLogFileURL() { return ''; }
|
|
8
10
|
async getLogFileContent() { return ''; }
|
|
9
|
-
async getBackupLogFileContent() { return
|
|
11
|
+
async getBackupLogFileContent() { return null; }
|
|
10
12
|
async clear() { }
|
|
11
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unvired/react-native-unvired-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Unvired SDK for React Native with logging, database, notifications, and file system support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,17 +24,12 @@
|
|
|
24
24
|
"author": "Unvired",
|
|
25
25
|
"license": "UNLICENSED",
|
|
26
26
|
"peerDependencies": {
|
|
27
|
+
"@dr.pogodin/react-native-fs": "^2.36.2",
|
|
27
28
|
"react": ">=16.8.0",
|
|
28
29
|
"react-native": ">=0.60.0",
|
|
29
|
-
"react-native-
|
|
30
|
-
"react-native-zip-archive": "^7.0.2",
|
|
31
|
-
"@react-native-windows/fs": "^1.0.2"
|
|
32
|
-
},
|
|
33
|
-
"peerDependenciesMeta": {
|
|
34
|
-
"@react-native-windows/fs": {
|
|
35
|
-
"optional": true
|
|
36
|
-
}
|
|
30
|
+
"react-native-zip-archive": "^7.0.2"
|
|
37
31
|
},
|
|
32
|
+
"peerDependenciesMeta": {},
|
|
38
33
|
"devDependencies": {
|
|
39
34
|
"@types/react": "^18.0.0",
|
|
40
35
|
"@types/react-native": "^0.72.0",
|
|
@@ -43,9 +38,7 @@
|
|
|
43
38
|
"prettier": "^3.0.0",
|
|
44
39
|
"typescript": "^5.0.0"
|
|
45
40
|
},
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"jszip": "^3.10.1"
|
|
48
|
-
},
|
|
41
|
+
"dependencies": {},
|
|
49
42
|
"repository": {},
|
|
50
43
|
"bugs": {},
|
|
51
44
|
"volta": {
|
|
@@ -54,4 +47,4 @@
|
|
|
54
47
|
"publishConfig": {
|
|
55
48
|
"access": "public"
|
|
56
49
|
}
|
|
57
|
-
}
|
|
50
|
+
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// LoggerNative.ts
|
|
2
|
-
import RNFS from 'react-native-fs';
|
|
3
|
-
import { zip } from 'react-native-zip-archive';
|
|
4
|
-
import { Platform } from 'react-native';
|
|
5
|
-
export class LoggerNative {
|
|
6
|
-
constructor() {
|
|
7
|
-
const dir = 'unvired_logs';
|
|
8
|
-
const file = 'applog.txt'; // main log file
|
|
9
|
-
if (Platform.OS === 'android') {
|
|
10
|
-
this.logDir = `${RNFS.DocumentDirectoryPath}/${dir}`;
|
|
11
|
-
}
|
|
12
|
-
else if (Platform.OS === 'ios') {
|
|
13
|
-
this.logDir = `${RNFS.LibraryDirectoryPath}/${dir}`;
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
this.logDir = '';
|
|
17
|
-
}
|
|
18
|
-
this.logFile = `${this.logDir}/${file}`;
|
|
19
|
-
this.tempFile = `${this.logDir}/templog.txt`;
|
|
20
|
-
this.backupZip = `${this.logDir}/backuplog.zip`;
|
|
21
|
-
}
|
|
22
|
-
async init() {
|
|
23
|
-
if (!this.logDir)
|
|
24
|
-
return;
|
|
25
|
-
if (!(await RNFS.exists(this.logDir)))
|
|
26
|
-
await RNFS.mkdir(this.logDir);
|
|
27
|
-
if (!(await RNFS.exists(this.logFile)))
|
|
28
|
-
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
29
|
-
}
|
|
30
|
-
async writeLine(line, maxSize) {
|
|
31
|
-
await this.ensureDir();
|
|
32
|
-
await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
|
|
33
|
-
const stat = await RNFS.stat(this.logFile);
|
|
34
|
-
if (Number(stat.size) >= maxSize) {
|
|
35
|
-
await this.rotateLog();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async ensureDir() {
|
|
39
|
-
if (!this.logDir)
|
|
40
|
-
return;
|
|
41
|
-
if (!(await RNFS.exists(this.logDir)))
|
|
42
|
-
await RNFS.mkdir(this.logDir);
|
|
43
|
-
}
|
|
44
|
-
async rotateLog() {
|
|
45
|
-
try {
|
|
46
|
-
// Step 1: Move current log → temp
|
|
47
|
-
if (await RNFS.exists(this.tempFile))
|
|
48
|
-
await RNFS.unlink(this.tempFile);
|
|
49
|
-
await RNFS.moveFile(this.logFile, this.tempFile);
|
|
50
|
-
// Step 2: Remove old backup.zip
|
|
51
|
-
if (await RNFS.exists(this.backupZip))
|
|
52
|
-
await RNFS.unlink(this.backupZip);
|
|
53
|
-
// Step 3: Zip temp → backup.zip
|
|
54
|
-
await zip(this.tempFile, this.backupZip);
|
|
55
|
-
// Step 4: Clear main log file
|
|
56
|
-
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
57
|
-
// Step 5: Remove temp
|
|
58
|
-
if (await RNFS.exists(this.tempFile))
|
|
59
|
-
await RNFS.unlink(this.tempFile);
|
|
60
|
-
console.log('✔ Log rotated to backup.zip successfully');
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
console.error('❌ Logger rotation error:', e);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// ------------------------
|
|
67
|
-
// Public accessors
|
|
68
|
-
// ------------------------
|
|
69
|
-
async getLogFileURL() {
|
|
70
|
-
return this.logFile;
|
|
71
|
-
}
|
|
72
|
-
async getLogFileContent() {
|
|
73
|
-
return (await RNFS.exists(this.logFile)) ? RNFS.readFile(this.logFile, 'utf8') : '';
|
|
74
|
-
}
|
|
75
|
-
async getBackupLogFileContent() {
|
|
76
|
-
return (await RNFS.exists(this.backupZip)) ? RNFS.readFile(this.backupZip, 'base64') : null;
|
|
77
|
-
}
|
|
78
|
-
async clear() {
|
|
79
|
-
if (await RNFS.exists(this.logFile))
|
|
80
|
-
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
81
|
-
if (await RNFS.exists(this.backupZip))
|
|
82
|
-
await RNFS.unlink(this.backupZip);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare class LoggerWindows {
|
|
2
|
-
private logFilePath;
|
|
3
|
-
private backupZipPath;
|
|
4
|
-
init(): Promise<void>;
|
|
5
|
-
writeLine(line: string, maxSize: number): Promise<void>;
|
|
6
|
-
getLogFileURL(): Promise<string>;
|
|
7
|
-
getLogFileContent(): any;
|
|
8
|
-
getBackupLogFileContent(): any;
|
|
9
|
-
clear(): Promise<void>;
|
|
10
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// LoggerWindows.ts
|
|
2
|
-
import JSZip from 'jszip';
|
|
3
|
-
const WindowsFS = require('@react-native-windows/fs');
|
|
4
|
-
export class LoggerWindows {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.logFilePath = 'C:\\temp\\unvired-sdk.log';
|
|
7
|
-
this.backupZipPath = 'C:\\temp\\unvired-sdk-backup.zip';
|
|
8
|
-
}
|
|
9
|
-
async init() {
|
|
10
|
-
if (!(await WindowsFS.exists(this.logFilePath))) {
|
|
11
|
-
await WindowsFS.writeFile(this.logFilePath, '');
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
async writeLine(line, maxSize) {
|
|
15
|
-
const old = await WindowsFS.readFile(this.logFilePath, 'utf8');
|
|
16
|
-
await WindowsFS.writeFile(this.logFilePath, old + line + '\n');
|
|
17
|
-
const stat = await WindowsFS.stat(this.logFilePath);
|
|
18
|
-
if (stat.size < maxSize)
|
|
19
|
-
return;
|
|
20
|
-
if (await WindowsFS.exists(this.backupZipPath)) {
|
|
21
|
-
await WindowsFS.unlink(this.backupZipPath);
|
|
22
|
-
}
|
|
23
|
-
const zip = new JSZip();
|
|
24
|
-
zip.file('unvired-sdk.log', old);
|
|
25
|
-
const zipped = await zip.generateAsync({ type: 'uint8array' });
|
|
26
|
-
await WindowsFS.writeFile(this.backupZipPath, zipped);
|
|
27
|
-
await WindowsFS.writeFile(this.logFilePath, '');
|
|
28
|
-
}
|
|
29
|
-
async getLogFileURL() {
|
|
30
|
-
return this.logFilePath;
|
|
31
|
-
}
|
|
32
|
-
getLogFileContent() {
|
|
33
|
-
return WindowsFS.readFile(this.logFilePath, 'utf8');
|
|
34
|
-
}
|
|
35
|
-
getBackupLogFileContent() {
|
|
36
|
-
return WindowsFS.readFile(this.backupZipPath, 'base64');
|
|
37
|
-
}
|
|
38
|
-
async clear() {
|
|
39
|
-
await WindowsFS.writeFile(this.logFilePath, '');
|
|
40
|
-
if (await WindowsFS.exists(this.backupZipPath)) {
|
|
41
|
-
await WindowsFS.unlink(this.backupZipPath);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface PlatformInterface {
|
|
2
|
-
// Logger
|
|
3
|
-
logInfo(className: string, methodName: string, message: string): void;
|
|
4
|
-
logError(className: string, methodName: string, message: string): void;
|
|
5
|
-
logDebug(className: string, methodName: string, message: string): void;
|
|
6
|
-
setLogLevel(level: string): void;
|
|
7
|
-
getLogFileURL(): Promise<string>;
|
|
8
|
-
getLogFileContent(): Promise<string>;
|
|
9
|
-
getBackupLogFileContent(): Promise<string>;
|
|
10
|
-
clearLogFile(): Promise<void>;
|
|
11
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { PlatformInterface } from './PlatformInterface';
|
|
2
|
-
import { logger } from '../src/logger/Logger';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export class ReactNativePlatformAdapter implements PlatformInterface {
|
|
6
|
-
|
|
7
|
-
// Logger
|
|
8
|
-
logInfo(className: string, methodName: string, message: string): void {
|
|
9
|
-
logger.logInfo(className, methodName, message);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
logError(className: string, methodName: string, message: string): void {
|
|
13
|
-
logger.logError(className, methodName, message);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
logDebug(className: string, methodName: string, message: string): void {
|
|
17
|
-
logger.logDebug(className, methodName, message);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
setLogLevel(level: string): void {
|
|
21
|
-
logger.setLogLevel(level);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async getLogFileURL(): Promise<string> {
|
|
25
|
-
return await logger.getLogFileURL();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async getLogFileContent(): Promise<string> {
|
|
29
|
-
return await logger.getLogFileContent();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async getBackupLogFileContent(): Promise<string> {
|
|
33
|
-
return await logger.getBackupLogFileContent();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async clearLogFile(): Promise<void> {
|
|
37
|
-
await logger.clearLogFile();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|