@unvired/react-native-unvired-sdk 0.0.11 → 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.0011
1
+ R-0.000.0012
@@ -2,6 +2,7 @@
2
2
  import { Platform } from 'react-native';
3
3
  import { LoggerNative } from './services/LoggerNative';
4
4
  import { LoggerWeb } from './services/LoggerWeb';
5
+ import { LoggerWindows } from './services/LoggerWindows';
5
6
  export const MAX_LOG_SIZE = 100 * 1024 * 1024;
6
7
  export class BaseLogger {
7
8
  constructor() {
@@ -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
  // -------------------------
@@ -27,8 +27,8 @@ 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';
31
- const file = 'applog_local.txt'; // main log file
30
+ const dir = 'unvired_logs';
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');
34
34
  this.logDir = '';
@@ -47,7 +47,7 @@ export class LoggerNative {
47
47
  this.logDir = '';
48
48
  }
49
49
  this.logFile = `${this.logDir}/${file}`;
50
- this.backupZip = `${this.logDir}/backuplog_local.zip`;
50
+ this.backupZip = `${this.logDir}/backuplog.zip`;
51
51
  }
52
52
  async init() {
53
53
  if (!this.isAvailable || !this.logDir || !RNFS)
@@ -188,8 +188,8 @@ export class LoggerNative {
188
188
  if (!this.isAvailable || !RNFS)
189
189
  return null;
190
190
  try {
191
- // Force rotation to ensure current logs are zipped
192
- await this.rotateLog();
191
+ // Do NOT force rotation. Only return existing backup if present.
192
+ // await this.rotateLog();
193
193
  if (await RNFS.exists(this.backupZip)) {
194
194
  // Return base64 content for download/upload
195
195
  return await RNFS.readFile(this.backupZip, 'base64');
@@ -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.11",
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
+ }