@unvired/react-native-unvired-sdk 0.0.10 → 0.0.12

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 CHANGED
@@ -1 +1 @@
1
- R-0.000.0010
1
+ R-0.000.0012
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { Logger, logger } from './logger/Logger';
2
- export type { LogLevel, ILogger, ILoggerImplementation, ILoggerConfig, ILogEntry, ILogFileInfo } from './logger/Logger';
2
+ export type { LogLevel, ILogger, ILoggerImplementation, } from './logger/Logger';
3
3
  export { default } from './logger/Logger';
@@ -16,13 +16,4 @@ export declare class BaseLogger {
16
16
  getLogFileContent(): Promise<string>;
17
17
  getBackupLogFileContent(): Promise<string | null>;
18
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;
28
19
  }
@@ -2,7 +2,8 @@
2
2
  import { Platform } from 'react-native';
3
3
  import { LoggerNative } from './services/LoggerNative';
4
4
  import { LoggerWeb } from './services/LoggerWeb';
5
- export const MAX_LOG_SIZE = 1 * 1024 * 1024;
5
+ import { LoggerWindows } from './services/LoggerWindows';
6
+ export const MAX_LOG_SIZE = 100 * 1024 * 1024;
6
7
  export class BaseLogger {
7
8
  constructor() {
8
9
  this.logLevel = 'info';
@@ -10,9 +11,7 @@ export class BaseLogger {
10
11
  this.impl = new LoggerWeb();
11
12
  }
12
13
  else if (Platform.OS === 'windows') {
13
- // TODO: Implement LoggerWindows
14
- console.warn('Windows logger not implemented yet, using web logger');
15
- this.impl = new LoggerWeb();
14
+ this.impl = new LoggerWindows();
16
15
  }
17
16
  else {
18
17
  this.impl = new LoggerNative();
@@ -24,7 +23,7 @@ export class BaseLogger {
24
23
  // -------------------------
25
24
  format(className, methodName, message, level) {
26
25
  const ts = new Date().toISOString();
27
- return `--[${ts}] [${level.toUpperCase()}] [${className}] [${methodName}] ${message}`;
26
+ return `[${ts}] [${level.toUpperCase()}] [${className}] [${methodName}] ${message}`;
28
27
  }
29
28
  setLogLevel(level) {
30
29
  this.logLevel = level;
@@ -46,17 +45,13 @@ export class BaseLogger {
46
45
  return;
47
46
  const line = this.format(className, methodName, message, level);
48
47
  // console always
48
+ /*
49
49
  switch (level) {
50
- case 'debug':
51
- console.debug(line);
52
- break;
53
- case 'info':
54
- console.info(line);
55
- break;
56
- case 'error':
57
- console.error(line);
58
- break;
50
+ case 'debug': console.debug(line); break;
51
+ case 'info': console.info(line); break;
52
+ case 'error': console.error(line); break;
59
53
  }
54
+ */
60
55
  await this.impl.writeLine(line, MAX_LOG_SIZE);
61
56
  }
62
57
  // -------------------------
@@ -72,47 +67,4 @@ export class BaseLogger {
72
67
  getLogFileContent() { return this.impl.getLogFileContent(); }
73
68
  getBackupLogFileContent() { return this.impl.getBackupLogFileContent(); }
74
69
  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
- }
118
70
  }
@@ -2,6 +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 type { ILogger, ILoggerImplementation, ILoggerConfig, ILogEntry, ILogFileInfo, } from './Logger.types';
5
+ export type { ILogger, ILoggerImplementation, } from './Logger.types';
6
6
  export type { LogLevel };
7
7
  export default logger;
@@ -13,7 +13,6 @@ export interface ILoggerImplementation {
13
13
  backupZip?: string;
14
14
  init(): Promise<void>;
15
15
  writeLine(line: string, maxSize: number): Promise<void>;
16
- rotateLog?(): Promise<void>;
17
16
  getLogFileURL(): Promise<string>;
18
17
  getLogFileContent(): Promise<string>;
19
18
  getBackupLogFileContent(): Promise<string | null>;
@@ -24,48 +23,12 @@ export interface ILoggerImplementation {
24
23
  * This is what consumers of the logger package should use
25
24
  */
26
25
  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>;
26
+ setLogLevel(level: LogLevel): void;
27
+ logDebug(className: string, methodName: string, message: string): void;
28
+ logInfo(className: string, methodName: string, message: string): void;
29
+ logError(className: string, methodName: string, message: string): void;
30
+ getLogFileURL(): Promise<string>;
31
+ getLogFileContent(): Promise<string>;
41
32
  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;
33
+ clearLogFile(): Promise<void>;
71
34
  }
@@ -9,9 +9,9 @@ export declare class LoggerNative implements ILoggerImplementation {
9
9
  init(): Promise<void>;
10
10
  writeLine(line: string, maxSize: number): Promise<void>;
11
11
  private ensureDir;
12
- rotateLog(): Promise<void>;
12
+ private rotateLog;
13
13
  getLogFileURL(): Promise<string>;
14
14
  getLogFileContent(): Promise<any>;
15
- getBackupLogFileContent(): Promise<string | null>;
15
+ getBackupLogFileContent(): Promise<any>;
16
16
  clear(): Promise<void>;
17
17
  }
@@ -27,7 +27,7 @@ export class LoggerNative {
27
27
  constructor() {
28
28
  this.isRotating = false; // Rotation lock to prevent concurrent rotations
29
29
  this.isAvailable = false;
30
- const dir = 'unvired_logs_local';
30
+ const dir = 'unvired_logs';
31
31
  const file = 'applog.txt'; // main log file
32
32
  if (!RNFS) {
33
33
  console.warn('⚠️ LoggerNative: RNFS not available, logging to file system disabled');
@@ -174,7 +174,10 @@ export class LoggerNative {
174
174
  if (!this.isAvailable || !RNFS)
175
175
  return '';
176
176
  try {
177
- return (await RNFS.exists(this.logFile)) ? RNFS.readFile(this.logFile, 'utf8') : '';
177
+ if (await RNFS.exists(this.logFile)) {
178
+ return await RNFS.readFile(this.logFile, 'utf8');
179
+ }
180
+ return '';
178
181
  }
179
182
  catch (e) {
180
183
  console.error('❌ LoggerNative getLogFileContent error:', e);
@@ -185,9 +188,11 @@ export class LoggerNative {
185
188
  if (!this.isAvailable || !RNFS)
186
189
  return null;
187
190
  try {
191
+ // Do NOT force rotation. Only return existing backup if present.
192
+ // await this.rotateLog();
188
193
  if (await RNFS.exists(this.backupZip)) {
189
- const stat = await RNFS.stat(this.backupZip);
190
- return `${stat.size}`;
194
+ // Return base64 content for download/upload
195
+ return await RNFS.readFile(this.backupZip, 'base64');
191
196
  }
192
197
  return null;
193
198
  }
@@ -0,0 +1,20 @@
1
+ import type { ILoggerImplementation } from '../Logger.types';
2
+ export declare class LoggerWindows implements ILoggerImplementation {
3
+ logDir: string;
4
+ logFile: string;
5
+ backupZip: string;
6
+ private isRotating;
7
+ private isAvailable;
8
+ constructor();
9
+ init(): Promise<void>;
10
+ private writeQueue;
11
+ private isWriting;
12
+ writeLine(line: string, maxSize: number): Promise<void>;
13
+ private processWriteQueue;
14
+ private ensureDir;
15
+ private rotateLog;
16
+ getLogFileURL(): Promise<string>;
17
+ getLogFileContent(): Promise<any>;
18
+ getBackupLogFileContent(): Promise<any>;
19
+ clear(): Promise<void>;
20
+ }
@@ -0,0 +1,225 @@
1
+ import * as pako from 'pako';
2
+ // Import with null safety
3
+ let RNFS = null;
4
+ try {
5
+ RNFS = require('@dr.pogodin/react-native-fs');
6
+ if (!RNFS || !RNFS.DocumentDirectoryPath) {
7
+ console.error('❌ @dr.pogodin/react-native-fs is not properly linked.');
8
+ RNFS = null;
9
+ }
10
+ }
11
+ catch (e) {
12
+ console.error('❌ @dr.pogodin/react-native-fs module not found.');
13
+ }
14
+ export class LoggerWindows {
15
+ constructor() {
16
+ this.isRotating = false; // Rotation lock to prevent concurrent rotations
17
+ this.isAvailable = false;
18
+ this.writeQueue = [];
19
+ this.isWriting = false;
20
+ const dir = 'unvired_logs';
21
+ const file = 'applog.txt'; // main log file
22
+ if (!RNFS) {
23
+ console.warn('⚠️ LoggerWindows: RNFS not available, logging to file system disabled');
24
+ this.logDir = '';
25
+ this.logFile = '';
26
+ this.backupZip = '';
27
+ return;
28
+ }
29
+ this.isAvailable = true;
30
+ // Windows specifically uses DocumentDirectoryPath (AppData/Local/Packages/.../LocalState)
31
+ this.logDir = `${RNFS.DocumentDirectoryPath}/${dir}`;
32
+ this.logFile = `${this.logDir}/${file}`;
33
+ this.backupZip = `${this.logDir}/backuplog.zip`;
34
+ }
35
+ async init() {
36
+ if (!this.isAvailable || !this.logDir || !RNFS)
37
+ return;
38
+ try {
39
+ if (!(await RNFS.exists(this.logDir)))
40
+ await RNFS.mkdir(this.logDir);
41
+ if (!(await RNFS.exists(this.logFile)))
42
+ await RNFS.writeFile(this.logFile, '', 'utf8');
43
+ }
44
+ catch (e) {
45
+ console.error('❌ LoggerWindows init error:', e);
46
+ }
47
+ }
48
+ async writeLine(line, maxSize) {
49
+ if (!this.isAvailable || !RNFS)
50
+ return;
51
+ // Push to queue
52
+ this.writeQueue.push(line);
53
+ // Trigger processing if not already running
54
+ this.processWriteQueue(maxSize);
55
+ }
56
+ async processWriteQueue(maxSize) {
57
+ if (this.isWriting)
58
+ return;
59
+ this.isWriting = true;
60
+ try {
61
+ await this.ensureDir();
62
+ while (this.writeQueue.length > 0) {
63
+ // Peek at the chunk of lines to write?
64
+ // Writing one by one is safe but slow.
65
+ // Let's write one by one for safety first.
66
+ const line = this.writeQueue.shift();
67
+ if (!line)
68
+ continue;
69
+ try {
70
+ await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
71
+ // Check rotation (only occasionally to avoid stat spam?)
72
+ // We can check every N writes or if queue is empty
73
+ if (!this.isRotating && this.writeQueue.length === 0) {
74
+ try {
75
+ const stat = await RNFS.stat(this.logFile);
76
+ if (Number(stat.size) >= maxSize) {
77
+ this.rotateLog();
78
+ }
79
+ }
80
+ catch (statErr) {
81
+ // ignore
82
+ }
83
+ }
84
+ }
85
+ catch (filesErr) {
86
+ // If file is locked, we might want to put line back?
87
+ // or just retry?
88
+ // For now, retry once then drop to avoid infinite loop
89
+ console.warn('⚠️ Write failed, retrying once:', filesErr.message);
90
+ try {
91
+ await new Promise(r => setTimeout(r, 50)); // small delay
92
+ await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
93
+ }
94
+ catch (retryErr) {
95
+ console.error('❌ Failed to write log line after retry:', retryErr);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ catch (e) {
101
+ console.error('❌ LoggerWindows queue error:', e);
102
+ }
103
+ finally {
104
+ this.isWriting = false;
105
+ // If logs came in while we were finishing, trigger again
106
+ if (this.writeQueue.length > 0) {
107
+ this.processWriteQueue(maxSize);
108
+ }
109
+ }
110
+ }
111
+ async ensureDir() {
112
+ if (!this.isAvailable || !this.logDir || !RNFS)
113
+ return;
114
+ try {
115
+ if (!(await RNFS.exists(this.logDir)))
116
+ await RNFS.mkdir(this.logDir);
117
+ }
118
+ catch (e) {
119
+ // Ignore if dir already exists race condition
120
+ }
121
+ }
122
+ async rotateLog() {
123
+ if (!this.isAvailable || !RNFS) {
124
+ return;
125
+ }
126
+ // Prevent concurrent rotations
127
+ if (this.isRotating) {
128
+ console.log('⏭️ Rotation already in progress, skipping...');
129
+ return;
130
+ }
131
+ this.isRotating = true;
132
+ try {
133
+ console.log('🔄 Starting log rotation (using pako gzip)...');
134
+ // Check if log file exists
135
+ if (!(await RNFS.exists(this.logFile))) {
136
+ return;
137
+ }
138
+ // Step 1: Read current log content
139
+ // Reading whole file into memory might be heavy for huge files,
140
+ // but for mobile logs generally acceptable (e.g. 5-10MB limit)
141
+ const content = await RNFS.readFile(this.logFile, 'utf8');
142
+ // Step 2: Compress content using Pako (gzip)
143
+ const binaryData = pako.gzip(content);
144
+ // Step 3: Convert binary to base64 for writing using Buffer (more robust)
145
+ // React Native environment usually has Buffer polyfill, or we use a chunked approach
146
+ const base64 = Buffer.from(binaryData).toString('base64');
147
+ // Step 4: Write to backup file (overwriting old one)
148
+ // We rename extensions to .gz to be accurate, or keep .zip if requirement is strict
149
+ // (but technically it IS a gzip stream, not a PKZip archive)
150
+ // User asked for base64 return, so storing it purely is fine.
151
+ await RNFS.writeFile(this.backupZip, base64, 'base64');
152
+ console.log('✅ Backup created successfully');
153
+ // Step 5: Clear current log
154
+ await RNFS.writeFile(this.logFile, '', 'utf8');
155
+ console.log('✔ Log rotation completed successfully');
156
+ }
157
+ catch (e) {
158
+ console.error('❌ Logger rotation error:', e);
159
+ // Recovery: ensure log file exists
160
+ try {
161
+ if (!(await RNFS.exists(this.logFile))) {
162
+ await RNFS.writeFile(this.logFile, '', 'utf8');
163
+ }
164
+ }
165
+ catch { }
166
+ }
167
+ finally {
168
+ this.isRotating = false;
169
+ }
170
+ }
171
+ // ------------------------
172
+ // Public accessors
173
+ // ------------------------
174
+ async getLogFileURL() {
175
+ if (!this.isAvailable)
176
+ return '';
177
+ return this.logFile;
178
+ }
179
+ async getLogFileContent() {
180
+ if (!this.isAvailable || !RNFS)
181
+ return '';
182
+ try {
183
+ if (await RNFS.exists(this.logFile)) {
184
+ return await RNFS.readFile(this.logFile, 'utf8');
185
+ }
186
+ return '';
187
+ }
188
+ catch (e) {
189
+ return '';
190
+ }
191
+ }
192
+ async getBackupLogFileContent() {
193
+ if (!this.isAvailable || !RNFS)
194
+ return null;
195
+ try {
196
+ // Do NOT force rotation. Only return existing backup if present.
197
+ // await this.rotateLog();
198
+ if (await RNFS.exists(this.backupZip)) {
199
+ // It's already written as base64 in rotateLog step via writeFile('base64'),
200
+ // but readFile('base64') reads the file content and returns base64.
201
+ // Wait, writeFile(path, data, 'base64') expects data to BE base64.
202
+ // readFile(path, 'base64') reads binary and RETURNS base64.
203
+ // Yes, so we can just read it back.
204
+ return await RNFS.readFile(this.backupZip, 'base64');
205
+ }
206
+ return null;
207
+ }
208
+ catch (e) {
209
+ console.error('❌ LoggerWindows getBackupLogFileContent error:', e);
210
+ return null;
211
+ }
212
+ }
213
+ async clear() {
214
+ if (!this.isAvailable || !RNFS)
215
+ return;
216
+ try {
217
+ await RNFS.writeFile(this.logFile, '', 'utf8');
218
+ if (await RNFS.exists(this.backupZip))
219
+ await RNFS.unlink(this.backupZip);
220
+ }
221
+ catch (e) {
222
+ // silent
223
+ }
224
+ }
225
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unvired/react-native-unvired-sdk",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
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",
@@ -38,7 +38,10 @@
38
38
  "prettier": "^3.0.0",
39
39
  "typescript": "^5.0.0"
40
40
  },
41
- "dependencies": {},
41
+ "dependencies": {
42
+ "@types/pako": "^2.0.4",
43
+ "pako": "^2.1.0"
44
+ },
42
45
  "repository": {},
43
46
  "bugs": {},
44
47
  "volta": {
@@ -47,4 +50,4 @@
47
50
  "publishConfig": {
48
51
  "access": "public"
49
52
  }
50
- }
53
+ }