@mongrov/core 0.1.0
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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/context/logging-provider.d.ts +11 -0
- package/dist/context/logging-provider.d.ts.map +1 -0
- package/dist/context/logging-provider.js +43 -0
- package/dist/context/logging-provider.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +159 -0
- package/dist/logger.js.map +1 -0
- package/dist/network-state.d.ts +13 -0
- package/dist/network-state.d.ts.map +1 -0
- package/dist/network-state.js +85 -0
- package/dist/network-state.js.map +1 -0
- package/dist/offline-queue.d.ts +30 -0
- package/dist/offline-queue.d.ts.map +1 -0
- package/dist/offline-queue.js +121 -0
- package/dist/offline-queue.js.map +1 -0
- package/dist/transports/file.d.ts +19 -0
- package/dist/transports/file.d.ts.map +1 -0
- package/dist/transports/file.js +136 -0
- package/dist/transports/file.js.map +1 -0
- package/dist/transports/index.d.ts +4 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +4 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/ring-buffer.d.ts +15 -0
- package/dist/transports/ring-buffer.d.ts.map +1 -0
- package/dist/transports/ring-buffer.js +66 -0
- package/dist/transports/ring-buffer.js.map +1 -0
- package/dist/transports/webhook.d.ts +18 -0
- package/dist/transports/webhook.d.ts.map +1 -0
- package/dist/transports/webhook.js +88 -0
- package/dist/transports/webhook.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { addNetworkStateListener, getNetworkState } from './network-state';
|
|
2
|
+
const QUEUE_KEY = '@mongrov/log-queue';
|
|
3
|
+
const DEFAULT_MAX_SIZE = 500;
|
|
4
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
5
|
+
const BASE_DELAY_MS = 1000;
|
|
6
|
+
function getMMKV() {
|
|
7
|
+
try {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
9
|
+
const { MMKV } = require('react-native-mmkv');
|
|
10
|
+
return new MMKV();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error('@mongrov/core OfflineQueue requires react-native-mmkv as a peer dependency');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class OfflineQueue {
|
|
17
|
+
constructor(sendFn, options) {
|
|
18
|
+
this.queue = [];
|
|
19
|
+
this.networkSubscription = null;
|
|
20
|
+
this.flushing = false;
|
|
21
|
+
this.sendFn = sendFn;
|
|
22
|
+
this.maxSize = options?.maxSize ?? DEFAULT_MAX_SIZE;
|
|
23
|
+
this.maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
24
|
+
this.storage = options?.storage ?? getMMKV();
|
|
25
|
+
this.loadFromStorage();
|
|
26
|
+
this.listenForConnectivity();
|
|
27
|
+
}
|
|
28
|
+
enqueue(entries) {
|
|
29
|
+
this.queue.push(...entries);
|
|
30
|
+
// Drop oldest entries if over max size (FIFO)
|
|
31
|
+
if (this.queue.length > this.maxSize) {
|
|
32
|
+
this.queue = this.queue.slice(this.queue.length - this.maxSize);
|
|
33
|
+
}
|
|
34
|
+
this.saveToStorage();
|
|
35
|
+
}
|
|
36
|
+
async flush() {
|
|
37
|
+
if (this.flushing || this.queue.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const networkState = await getNetworkState();
|
|
40
|
+
if (!networkState.isConnected)
|
|
41
|
+
return;
|
|
42
|
+
this.flushing = true;
|
|
43
|
+
try {
|
|
44
|
+
// Take a snapshot of entries to send
|
|
45
|
+
const batch = [...this.queue];
|
|
46
|
+
await this.sendWithRetry(batch);
|
|
47
|
+
// On success, remove sent entries (preserving any enqueued during send)
|
|
48
|
+
this.queue = this.queue.slice(batch.length);
|
|
49
|
+
this.saveToStorage();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Retries exhausted — entries stay in queue for next flush attempt
|
|
53
|
+
this.saveToStorage();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
this.flushing = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
getQueueSize() {
|
|
60
|
+
return this.queue.length;
|
|
61
|
+
}
|
|
62
|
+
destroy() {
|
|
63
|
+
if (this.networkSubscription) {
|
|
64
|
+
this.networkSubscription.remove();
|
|
65
|
+
this.networkSubscription = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async sendWithRetry(entries) {
|
|
69
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
70
|
+
try {
|
|
71
|
+
await this.sendFn(entries);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (attempt < this.maxRetries - 1) {
|
|
76
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// All retries exhausted — throw so caller knows entries were NOT sent
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
loadFromStorage() {
|
|
87
|
+
try {
|
|
88
|
+
const raw = this.storage.getString(QUEUE_KEY);
|
|
89
|
+
if (raw) {
|
|
90
|
+
const parsed = JSON.parse(raw);
|
|
91
|
+
if (Array.isArray(parsed)) {
|
|
92
|
+
this.queue = parsed;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
this.queue = [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
saveToStorage() {
|
|
101
|
+
try {
|
|
102
|
+
if (this.queue.length === 0) {
|
|
103
|
+
this.storage.delete(QUEUE_KEY);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.storage.set(QUEUE_KEY, JSON.stringify(this.queue));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Storage failures are non-critical
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
listenForConnectivity() {
|
|
114
|
+
this.networkSubscription = addNetworkStateListener((state) => {
|
|
115
|
+
if (state.isConnected) {
|
|
116
|
+
this.flush().catch(() => { });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=offline-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-queue.js","sourceRoot":"","sources":["../src/offline-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LogEntry, LogTransport, FileConfig } from '../types';
|
|
2
|
+
export declare class FileTransport implements LogTransport {
|
|
3
|
+
readonly name = "file";
|
|
4
|
+
private readonly directory;
|
|
5
|
+
private readonly maxSizeMB;
|
|
6
|
+
private readonly retentionDays;
|
|
7
|
+
private initialized;
|
|
8
|
+
private writeChain;
|
|
9
|
+
private lastCleanup;
|
|
10
|
+
private readonly cleanupIntervalMs;
|
|
11
|
+
constructor(config?: FileConfig);
|
|
12
|
+
send(entries: LogEntry[]): Promise<void>;
|
|
13
|
+
private doWrite;
|
|
14
|
+
getLogFiles(): Promise<string[]>;
|
|
15
|
+
readFile(filename: string): Promise<string>;
|
|
16
|
+
private ensureDirectory;
|
|
17
|
+
private cleanupIfNeeded;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAqClE,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,IAAI,UAAS;IAEtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,UAAU,CAAmC;IAGrD,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAE/B,MAAM,CAAC,EAAE,UAAU;IAQzB,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAOhC,OAAO;IA8Bf,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAahC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAKnC,eAAe;YAUf,eAAe;CA4C9B"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
let FileSystemModule = null;
|
|
2
|
+
function getFileSystem() {
|
|
3
|
+
if (!FileSystemModule) {
|
|
4
|
+
try {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
6
|
+
FileSystemModule = require('expo-file-system');
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
throw new Error('@mongrov/core FileTransport requires expo-file-system as a peer dependency');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return FileSystemModule;
|
|
13
|
+
}
|
|
14
|
+
function formatDate(date) {
|
|
15
|
+
const y = date.getFullYear();
|
|
16
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
17
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
18
|
+
return `${y}-${m}-${d}`;
|
|
19
|
+
}
|
|
20
|
+
export class FileTransport {
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.name = 'file';
|
|
23
|
+
this.initialized = false;
|
|
24
|
+
// Serial write queue to prevent concurrent read-then-write race conditions
|
|
25
|
+
this.writeChain = Promise.resolve();
|
|
26
|
+
// Throttle cleanup to once per minute
|
|
27
|
+
this.lastCleanup = 0;
|
|
28
|
+
this.cleanupIntervalMs = 60000;
|
|
29
|
+
const fs = getFileSystem();
|
|
30
|
+
this.directory =
|
|
31
|
+
config?.directory ?? `${fs.documentDirectory ?? ''}logs/`;
|
|
32
|
+
this.maxSizeMB = config?.maxSizeMB ?? 5;
|
|
33
|
+
this.retentionDays = config?.retentionDays ?? 7;
|
|
34
|
+
}
|
|
35
|
+
async send(entries) {
|
|
36
|
+
// Chain writes to serialize file access
|
|
37
|
+
const op = this.writeChain.then(() => this.doWrite(entries));
|
|
38
|
+
this.writeChain = op.catch(() => { });
|
|
39
|
+
return op;
|
|
40
|
+
}
|
|
41
|
+
async doWrite(entries) {
|
|
42
|
+
try {
|
|
43
|
+
await this.ensureDirectory();
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
if (now - this.lastCleanup >= this.cleanupIntervalMs) {
|
|
46
|
+
await this.cleanupIfNeeded();
|
|
47
|
+
this.lastCleanup = now;
|
|
48
|
+
}
|
|
49
|
+
const lines = entries.map((e) => JSON.stringify(e)).join('\n') + '\n';
|
|
50
|
+
const filename = `logs-${formatDate(new Date())}.txt`;
|
|
51
|
+
const filePath = `${this.directory}${filename}`;
|
|
52
|
+
const fs = getFileSystem();
|
|
53
|
+
const info = await fs.getInfoAsync(filePath);
|
|
54
|
+
if (info.exists) {
|
|
55
|
+
// Append by reading existing content + new lines
|
|
56
|
+
const existing = await fs.readAsStringAsync(filePath);
|
|
57
|
+
await fs.writeAsStringAsync(filePath, existing + lines);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await fs.writeAsStringAsync(filePath, lines);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// Logging should never crash the app
|
|
65
|
+
console.warn('[FileTransport] Write failed:', error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async getLogFiles() {
|
|
69
|
+
try {
|
|
70
|
+
const fs = getFileSystem();
|
|
71
|
+
const files = await fs.readDirectoryAsync(this.directory);
|
|
72
|
+
return files
|
|
73
|
+
.filter((f) => f.startsWith('logs-') && f.endsWith('.txt'))
|
|
74
|
+
.sort()
|
|
75
|
+
.reverse();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async readFile(filename) {
|
|
82
|
+
const fs = getFileSystem();
|
|
83
|
+
return fs.readAsStringAsync(`${this.directory}${filename}`);
|
|
84
|
+
}
|
|
85
|
+
async ensureDirectory() {
|
|
86
|
+
if (this.initialized)
|
|
87
|
+
return;
|
|
88
|
+
const fs = getFileSystem();
|
|
89
|
+
const info = await fs.getInfoAsync(this.directory);
|
|
90
|
+
if (!info.exists) {
|
|
91
|
+
await fs.makeDirectoryAsync(this.directory, { intermediates: true });
|
|
92
|
+
}
|
|
93
|
+
this.initialized = true;
|
|
94
|
+
}
|
|
95
|
+
async cleanupIfNeeded() {
|
|
96
|
+
try {
|
|
97
|
+
const fs = getFileSystem();
|
|
98
|
+
const files = await fs.readDirectoryAsync(this.directory);
|
|
99
|
+
const logFiles = files
|
|
100
|
+
.filter((f) => f.startsWith('logs-') && f.endsWith('.txt'))
|
|
101
|
+
.sort();
|
|
102
|
+
// Delete files older than retentionDays
|
|
103
|
+
const cutoffDate = new Date();
|
|
104
|
+
cutoffDate.setDate(cutoffDate.getDate() - this.retentionDays);
|
|
105
|
+
const cutoffStr = formatDate(cutoffDate);
|
|
106
|
+
for (const file of logFiles) {
|
|
107
|
+
const dateStr = file.replace('logs-', '').replace('.txt', '');
|
|
108
|
+
if (dateStr < cutoffStr) {
|
|
109
|
+
await fs.deleteAsync(`${this.directory}${file}`, { idempotent: true });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Check total size and delete oldest if exceeding maxSizeMB
|
|
113
|
+
let totalSize = 0;
|
|
114
|
+
const remaining = (await fs.readDirectoryAsync(this.directory))
|
|
115
|
+
.filter((f) => f.startsWith('logs-') && f.endsWith('.txt'))
|
|
116
|
+
.sort();
|
|
117
|
+
for (const file of remaining) {
|
|
118
|
+
const info = await fs.getInfoAsync(`${this.directory}${file}`);
|
|
119
|
+
totalSize += info.size ?? 0;
|
|
120
|
+
}
|
|
121
|
+
const maxSizeBytes = this.maxSizeMB * 1024 * 1024;
|
|
122
|
+
let i = 0;
|
|
123
|
+
while (totalSize > maxSizeBytes && i < remaining.length) {
|
|
124
|
+
const file = remaining[i];
|
|
125
|
+
const info = await fs.getInfoAsync(`${this.directory}${file}`);
|
|
126
|
+
await fs.deleteAsync(`${this.directory}${file}`, { idempotent: true });
|
|
127
|
+
totalSize -= info.size ?? 0;
|
|
128
|
+
i++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Cleanup failures are non-critical
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transports/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transports/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LogEntry, LogFilter, LogTransport } from '../types';
|
|
2
|
+
export declare class RingBufferTransport implements LogTransport {
|
|
3
|
+
readonly name = "ring-buffer";
|
|
4
|
+
private buffer;
|
|
5
|
+
private head;
|
|
6
|
+
private count;
|
|
7
|
+
private readonly maxSize;
|
|
8
|
+
constructor(maxSize?: number);
|
|
9
|
+
send(entries: LogEntry[]): Promise<void>;
|
|
10
|
+
getEntries(filter?: LogFilter): LogEntry[];
|
|
11
|
+
clear(): void;
|
|
12
|
+
toJSON(): string;
|
|
13
|
+
private applyFilter;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=ring-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ring-buffer.d.ts","sourceRoot":"","sources":["../../src/transports/ring-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAY,YAAY,EAAE,MAAM,UAAU,CAAA;AAS3E,qBAAa,mBAAoB,YAAW,YAAY;IACtD,QAAQ,CAAC,IAAI,iBAAgB;IAE7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;gBAEpB,OAAO,SAAO;IAKpB,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9C,UAAU,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE;IAe1C,KAAK,IAAI,IAAI;IAMb,MAAM,IAAI,MAAM;IAIhB,OAAO,CAAC,WAAW;CA0BpB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const LEVEL_ORDER = {
|
|
2
|
+
debug: 0,
|
|
3
|
+
info: 1,
|
|
4
|
+
warn: 2,
|
|
5
|
+
error: 3,
|
|
6
|
+
};
|
|
7
|
+
export class RingBufferTransport {
|
|
8
|
+
constructor(maxSize = 1000) {
|
|
9
|
+
this.name = 'ring-buffer';
|
|
10
|
+
this.head = 0;
|
|
11
|
+
this.count = 0;
|
|
12
|
+
this.maxSize = maxSize;
|
|
13
|
+
this.buffer = new Array(maxSize).fill(null);
|
|
14
|
+
}
|
|
15
|
+
async send(entries) {
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
this.buffer[this.head] = entry;
|
|
18
|
+
this.head = (this.head + 1) % this.maxSize;
|
|
19
|
+
if (this.count < this.maxSize) {
|
|
20
|
+
this.count++;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
getEntries(filter) {
|
|
25
|
+
const entries = [];
|
|
26
|
+
// Read from newest to oldest
|
|
27
|
+
for (let i = 0; i < this.count; i++) {
|
|
28
|
+
const index = (this.head - 1 - i + this.maxSize) % this.maxSize;
|
|
29
|
+
const entry = this.buffer[index];
|
|
30
|
+
if (entry) {
|
|
31
|
+
entries.push(entry);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return this.applyFilter(entries, filter);
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
this.buffer = new Array(this.maxSize).fill(null);
|
|
38
|
+
this.head = 0;
|
|
39
|
+
this.count = 0;
|
|
40
|
+
}
|
|
41
|
+
toJSON() {
|
|
42
|
+
return JSON.stringify(this.getEntries());
|
|
43
|
+
}
|
|
44
|
+
applyFilter(entries, filter) {
|
|
45
|
+
if (!filter)
|
|
46
|
+
return entries;
|
|
47
|
+
let result = entries;
|
|
48
|
+
if (filter.level) {
|
|
49
|
+
const minLevel = LEVEL_ORDER[filter.level];
|
|
50
|
+
result = result.filter((e) => LEVEL_ORDER[e.level] >= minLevel);
|
|
51
|
+
}
|
|
52
|
+
if (filter.since) {
|
|
53
|
+
const sinceTime = filter.since.getTime();
|
|
54
|
+
result = result.filter((e) => new Date(e.timestamp).getTime() >= sinceTime);
|
|
55
|
+
}
|
|
56
|
+
if (filter.search) {
|
|
57
|
+
const search = filter.search.toLowerCase();
|
|
58
|
+
result = result.filter((e) => e.message.toLowerCase().includes(search));
|
|
59
|
+
}
|
|
60
|
+
if (filter.limit && filter.limit > 0) {
|
|
61
|
+
result = result.slice(0, filter.limit);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=ring-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ring-buffer.js","sourceRoot":"","sources":["../../src/transports/ring-buffer.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAA;AAED,MAAM,OAAO,mBAAmB;IAQ9B,YAAY,OAAO,GAAG,IAAI;QAPjB,SAAI,GAAG,aAAa,CAAA;QAGrB,SAAI,GAAG,CAAC,CAAA;QACR,UAAK,GAAG,CAAC,CAAA;QAIf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAmB;QAC5B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;YAC9B,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;YAC1C,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,KAAK,EAAE,CAAA;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED,UAAU,CAAC,MAAkB;QAC3B,MAAM,OAAO,GAAe,EAAE,CAAA;QAE9B,6BAA6B;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;YAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAChC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;QACb,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IAChB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;IAC1C,CAAC;IAEO,WAAW,CAAC,OAAmB,EAAE,MAAkB;QACzD,IAAI,CAAC,MAAM;YAAE,OAAO,OAAO,CAAA;QAE3B,IAAI,MAAM,GAAG,OAAO,CAAA;QAEpB,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC1C,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAA;QACjE,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;YACxC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,CAAA;QAC7E,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;YAC1C,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;QACzE,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;QACxC,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LogEntry, LogTransport, WebhookConfig } from '../types';
|
|
2
|
+
export declare class WebhookTransport implements LogTransport {
|
|
3
|
+
readonly name = "webhook";
|
|
4
|
+
private readonly config;
|
|
5
|
+
private batch;
|
|
6
|
+
private timer;
|
|
7
|
+
private offlineQueue;
|
|
8
|
+
private flushing;
|
|
9
|
+
constructor(config: WebhookConfig);
|
|
10
|
+
send(entries: LogEntry[]): Promise<void>;
|
|
11
|
+
flush(): Promise<void>;
|
|
12
|
+
destroy(): Promise<void>;
|
|
13
|
+
private startTimer;
|
|
14
|
+
private stopTimer;
|
|
15
|
+
private flushBatch;
|
|
16
|
+
private postEntries;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/transports/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGrE,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,aAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAG2B;IAElD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,MAAM,EAAE,aAAa;IAkB3B,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;YAOH,UAAU;YAiBV,WAAW;CA2B1B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { OfflineQueue } from '../offline-queue';
|
|
2
|
+
export class WebhookTransport {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.name = 'webhook';
|
|
5
|
+
this.batch = [];
|
|
6
|
+
this.timer = null;
|
|
7
|
+
this.flushing = false;
|
|
8
|
+
this.config = {
|
|
9
|
+
url: config.url,
|
|
10
|
+
batchSize: config.batchSize ?? 10,
|
|
11
|
+
batchIntervalMs: config.batchIntervalMs ?? 5000,
|
|
12
|
+
maxRetries: config.maxRetries ?? 3,
|
|
13
|
+
headers: config.headers,
|
|
14
|
+
formatPayload: config.formatPayload,
|
|
15
|
+
};
|
|
16
|
+
this.offlineQueue = new OfflineQueue((entries) => this.postEntries(entries), { maxRetries: this.config.maxRetries });
|
|
17
|
+
this.startTimer();
|
|
18
|
+
}
|
|
19
|
+
async send(entries) {
|
|
20
|
+
this.batch.push(...entries);
|
|
21
|
+
if (this.batch.length >= this.config.batchSize) {
|
|
22
|
+
await this.flushBatch();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async flush() {
|
|
26
|
+
await this.flushBatch();
|
|
27
|
+
await this.offlineQueue.flush();
|
|
28
|
+
}
|
|
29
|
+
async destroy() {
|
|
30
|
+
this.stopTimer();
|
|
31
|
+
await this.flushBatch();
|
|
32
|
+
await this.offlineQueue.flush();
|
|
33
|
+
this.offlineQueue.destroy();
|
|
34
|
+
}
|
|
35
|
+
startTimer() {
|
|
36
|
+
this.timer = setInterval(() => {
|
|
37
|
+
if (this.batch.length > 0) {
|
|
38
|
+
this.flushBatch().catch(() => { });
|
|
39
|
+
}
|
|
40
|
+
}, this.config.batchIntervalMs);
|
|
41
|
+
}
|
|
42
|
+
stopTimer() {
|
|
43
|
+
if (this.timer) {
|
|
44
|
+
clearInterval(this.timer);
|
|
45
|
+
this.timer = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async flushBatch() {
|
|
49
|
+
if (this.flushing || this.batch.length === 0)
|
|
50
|
+
return;
|
|
51
|
+
this.flushing = true;
|
|
52
|
+
const entries = [...this.batch];
|
|
53
|
+
this.batch = [];
|
|
54
|
+
try {
|
|
55
|
+
await this.postEntries(entries);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Delegate to offline queue for retry
|
|
59
|
+
this.offlineQueue.enqueue(entries);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
this.flushing = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async postEntries(entries) {
|
|
66
|
+
const payload = this.config.formatPayload
|
|
67
|
+
? this.config.formatPayload(entries)
|
|
68
|
+
: { entries };
|
|
69
|
+
const response = await fetch(this.config.url, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
...this.config.headers,
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify(payload),
|
|
76
|
+
});
|
|
77
|
+
if (response.status >= 400 && response.status < 500) {
|
|
78
|
+
// Client error — drop entries, retrying won't help
|
|
79
|
+
console.warn(`[WebhookTransport] HTTP ${response.status}: dropping ${entries.length} entries`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
// 5xx — throw to trigger offline queue retry
|
|
84
|
+
throw new Error(`HTTP ${response.status}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/transports/webhook.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
+
export interface LogContext {
|
|
3
|
+
userId?: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
screenName?: string;
|
|
6
|
+
appVersion: string;
|
|
7
|
+
updateId?: string;
|
|
8
|
+
platform: 'ios' | 'android';
|
|
9
|
+
deviceId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface LogEntry {
|
|
12
|
+
id: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
level: LogLevel;
|
|
15
|
+
message: string;
|
|
16
|
+
data?: Record<string, unknown>;
|
|
17
|
+
context: LogContext;
|
|
18
|
+
}
|
|
19
|
+
export interface LogFilter {
|
|
20
|
+
level?: LogLevel;
|
|
21
|
+
since?: Date;
|
|
22
|
+
search?: string;
|
|
23
|
+
limit?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface LogTransport {
|
|
26
|
+
name: string;
|
|
27
|
+
send(entries: LogEntry[]): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface RingBufferConfig {
|
|
30
|
+
maxSize?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface FileConfig {
|
|
33
|
+
maxSizeMB?: number;
|
|
34
|
+
retentionDays?: number;
|
|
35
|
+
directory?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface WebhookConfig {
|
|
38
|
+
url: string;
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
batchSize?: number;
|
|
41
|
+
batchIntervalMs?: number;
|
|
42
|
+
maxRetries?: number;
|
|
43
|
+
formatPayload?: (entries: LogEntry[]) => unknown;
|
|
44
|
+
}
|
|
45
|
+
export interface LoggerConfig {
|
|
46
|
+
minLevel?: LogLevel;
|
|
47
|
+
ringBuffer?: RingBufferConfig | boolean;
|
|
48
|
+
file?: FileConfig | boolean;
|
|
49
|
+
webhook?: WebhookConfig;
|
|
50
|
+
transports?: LogTransport[];
|
|
51
|
+
appVersion: string;
|
|
52
|
+
updateId?: string;
|
|
53
|
+
/** @deprecated Use `onLog` instead. */
|
|
54
|
+
onError?: (entry: LogEntry) => void;
|
|
55
|
+
/** Called for every warn / error entry. Use for Sentry breadcrumbs, analytics, etc. */
|
|
56
|
+
onLog?: (entry: LogEntry) => void;
|
|
57
|
+
onException?: (error: Error, context?: Record<string, unknown>) => void;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE1D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,QAAQ,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,EAAE,UAAU,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,OAAO,CAAA;CACjD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAA;IACvC,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,UAAU,CAAC,EAAE,YAAY,EAAE,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACnC,uFAAuF;IACvF,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACxE"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|