smol-logs 1.0.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 +208 -0
- package/dist/blocks.d.ts +24 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +214 -0
- package/dist/blocks.js.map +1 -0
- package/dist/compressor.d.ts +26 -0
- package/dist/compressor.d.ts.map +1 -0
- package/dist/compressor.js +193 -0
- package/dist/compressor.js.map +1 -0
- package/dist/formatter.d.ts +14 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +111 -0
- package/dist/formatter.js.map +1 -0
- package/dist/hashing.d.ts +32 -0
- package/dist/hashing.d.ts.map +1 -0
- package/dist/hashing.js +89 -0
- package/dist/hashing.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -0
- package/dist/patterns.d.ts +30 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +305 -0
- package/dist/patterns.js.map +1 -0
- package/dist/rle.d.ts +36 -0
- package/dist/rle.d.ts.map +1 -0
- package/dist/rle.js +121 -0
- package/dist/rle.js.map +1 -0
- package/dist/timestamps.d.ts +43 -0
- package/dist/timestamps.d.ts.map +1 -0
- package/dist/timestamps.js +191 -0
- package/dist/timestamps.js.map +1 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# smol-logs
|
|
2
|
+
|
|
3
|
+
A CLI tool to compress verbose text output (logs, test output, build logs) into a human-readable, AI-friendly format. Reduces token usage when working with AI tools by intelligently encoding repetition.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g smol-logs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly with npx:
|
|
12
|
+
```bash
|
|
13
|
+
npx smol-logs
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Pipe input from stdin
|
|
20
|
+
cat test.log | smol
|
|
21
|
+
|
|
22
|
+
# Read from file
|
|
23
|
+
smol test.log
|
|
24
|
+
|
|
25
|
+
# Write to file
|
|
26
|
+
smol test.log -o compressed.txt
|
|
27
|
+
|
|
28
|
+
# Adjust similarity threshold for pattern detection
|
|
29
|
+
smol test.log -t 0.9
|
|
30
|
+
|
|
31
|
+
# Exclude metadata header
|
|
32
|
+
smol test.log --no-meta
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## How It Works
|
|
36
|
+
|
|
37
|
+
smol uses three compression strategies:
|
|
38
|
+
|
|
39
|
+
### 1. Run-Length Encoding (RLE)
|
|
40
|
+
Collapses consecutive identical lines:
|
|
41
|
+
|
|
42
|
+
**Input:**
|
|
43
|
+
```
|
|
44
|
+
Loading config...
|
|
45
|
+
Loading config...
|
|
46
|
+
Loading config...
|
|
47
|
+
Server started
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Output:**
|
|
51
|
+
```
|
|
52
|
+
Loading config...
|
|
53
|
+
↑ repeated 3 times
|
|
54
|
+
Server started
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Global Deduplication
|
|
58
|
+
Removes non-consecutive duplicate lines:
|
|
59
|
+
|
|
60
|
+
**Input:**
|
|
61
|
+
```
|
|
62
|
+
console.log
|
|
63
|
+
Message 1
|
|
64
|
+
console.log
|
|
65
|
+
Message 2
|
|
66
|
+
console.log
|
|
67
|
+
Message 3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Output:**
|
|
71
|
+
```
|
|
72
|
+
console.log
|
|
73
|
+
↑ appears 3 times
|
|
74
|
+
Message 1
|
|
75
|
+
Message 2
|
|
76
|
+
Message 3
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Pattern Templates
|
|
80
|
+
Detects near-duplicate lines and extracts variable parts:
|
|
81
|
+
|
|
82
|
+
**Input:**
|
|
83
|
+
```
|
|
84
|
+
[INFO] Server ready on port 8080
|
|
85
|
+
[INFO] Server ready on port 8081
|
|
86
|
+
[INFO] Server ready on port 8082
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Output:**
|
|
90
|
+
```
|
|
91
|
+
"[INFO] Server ready on port {port}"
|
|
92
|
+
↑ pattern matched 3 times: port = 8080, 8081, 8082
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Output Format
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
=== COMPRESSION: 1247 → 89 lines (93%) ===
|
|
99
|
+
|
|
100
|
+
Loading config...
|
|
101
|
+
↑ repeated 3 times
|
|
102
|
+
|
|
103
|
+
Server started
|
|
104
|
+
|
|
105
|
+
"[INFO] Server ready on port {port}"
|
|
106
|
+
↑ pattern matched 5 times: port = 8080, 8081, 8082, 8083, 8084
|
|
107
|
+
|
|
108
|
+
"[ERROR] Connection failed: {error}"
|
|
109
|
+
↑ pattern matched 12 times: error = timeout, refused, reset, ...3 more
|
|
110
|
+
↑ time range: 10:15:06 - 10:17:23
|
|
111
|
+
|
|
112
|
+
Done
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Format Elements
|
|
116
|
+
|
|
117
|
+
| Element | Syntax | Meaning |
|
|
118
|
+
|---------|--------|---------|
|
|
119
|
+
| Repeated line | `line` + `↑ repeated N times` | Line appeared N consecutive times |
|
|
120
|
+
| Deduplicated | `line` + `↑ appears N times` | Line appeared N times (non-consecutive) |
|
|
121
|
+
| Pattern | `"template"` + `↑ pattern matched` | Near-duplicate lines with variables |
|
|
122
|
+
| Unique line | just the line | Single occurrence |
|
|
123
|
+
|
|
124
|
+
## CLI Options
|
|
125
|
+
|
|
126
|
+
| Option | Description | Default |
|
|
127
|
+
|--------|-------------|---------|
|
|
128
|
+
| `-o, --output <file>` | Output file (stdout if not provided) | - |
|
|
129
|
+
| `-t, --threshold <n>` | Similarity threshold for patterns (0-1) | 0.8 |
|
|
130
|
+
| `--no-meta` | Exclude compression stats header | false |
|
|
131
|
+
| `-p, --max-patterns <n>` | Maximum patterns to extract | 10 |
|
|
132
|
+
| `-v, --verbose` | Show compression statistics | false |
|
|
133
|
+
|
|
134
|
+
## Examples
|
|
135
|
+
|
|
136
|
+
### Compress Test Output
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm test 2>&1 | smol
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Compress Build Logs
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run build 2>&1 | smol -o build-summary.txt
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Use with AI Tools
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Compress and copy to clipboard (macOS)
|
|
152
|
+
cat verbose.log | smol | pbcopy
|
|
153
|
+
|
|
154
|
+
# Compress and copy to clipboard (Windows)
|
|
155
|
+
type verbose.log | smol | clip
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## API Usage
|
|
159
|
+
|
|
160
|
+
You can also use the compressor programmatically:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { compress, compressVerbose } from 'smol-logs';
|
|
164
|
+
|
|
165
|
+
const input = `
|
|
166
|
+
Loading...
|
|
167
|
+
Loading...
|
|
168
|
+
Server started
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
// Basic compression
|
|
172
|
+
const output = compress(input);
|
|
173
|
+
console.log(output);
|
|
174
|
+
|
|
175
|
+
// With options
|
|
176
|
+
const output2 = compress(input, {
|
|
177
|
+
threshold: 0.9,
|
|
178
|
+
includeMeta: false,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// With statistics
|
|
182
|
+
const { output: result, stats } = compressVerbose(input);
|
|
183
|
+
console.log(`Compression: ${stats.totalLines} → ${stats.compressedItems} items`);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Clone the repository
|
|
190
|
+
git clone https://github.com/anthropics/smol-logs
|
|
191
|
+
cd smol-logs
|
|
192
|
+
|
|
193
|
+
# Install dependencies
|
|
194
|
+
npm install
|
|
195
|
+
|
|
196
|
+
# Run tests
|
|
197
|
+
npm test
|
|
198
|
+
|
|
199
|
+
# Build
|
|
200
|
+
npm run build
|
|
201
|
+
|
|
202
|
+
# Run in development
|
|
203
|
+
npm run dev sample.log
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
package/dist/blocks.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block detection for repeated sequences of lines
|
|
3
|
+
* Uses content-defined chunking concepts to find natural boundaries
|
|
4
|
+
* and detect repeated blocks
|
|
5
|
+
*/
|
|
6
|
+
import { LineInfo, DetectedBlock, BlockResult, CompressorOptions } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Detect repeated blocks of lines
|
|
9
|
+
* @param lines - Array of LineInfo
|
|
10
|
+
* @param options - Compressor options
|
|
11
|
+
* @returns BlockResult with detected blocks
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectBlocks(lines: LineInfo[], options: CompressorOptions): BlockResult;
|
|
14
|
+
/**
|
|
15
|
+
* Get block coverage info for debugging
|
|
16
|
+
* @param blocks - Detected blocks
|
|
17
|
+
* @param totalLines - Total number of lines
|
|
18
|
+
* @returns Coverage statistics
|
|
19
|
+
*/
|
|
20
|
+
export declare function getBlockCoverage(blocks: DetectedBlock[], totalLines: number): {
|
|
21
|
+
coveredLines: number;
|
|
22
|
+
percentage: string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AA0IlF;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,EAAE,iBAAiB,GACzB,WAAW,CA8Eb;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EAAE,EACvB,UAAU,EAAE,MAAM,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAe9C"}
|
package/dist/blocks.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Block detection for repeated sequences of lines
|
|
4
|
+
* Uses content-defined chunking concepts to find natural boundaries
|
|
5
|
+
* and detect repeated blocks
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.detectBlocks = detectBlocks;
|
|
9
|
+
exports.getBlockCoverage = getBlockCoverage;
|
|
10
|
+
const hashing_1 = require("./hashing");
|
|
11
|
+
/** Gear table for rolling hash (256 random values) */
|
|
12
|
+
const GEAR_TABLE = (() => {
|
|
13
|
+
// Deterministic pseudo-random values using a simple LCG
|
|
14
|
+
const table = [];
|
|
15
|
+
let seed = 0x12345678;
|
|
16
|
+
for (let i = 0; i < 256; i++) {
|
|
17
|
+
seed = Math.imul(seed, 1103515245) + 12345;
|
|
18
|
+
table.push(seed >>> 0);
|
|
19
|
+
}
|
|
20
|
+
return table;
|
|
21
|
+
})();
|
|
22
|
+
/**
|
|
23
|
+
* Compute a hash for a sequence of lines
|
|
24
|
+
* @param lines - Array of line strings
|
|
25
|
+
* @returns Hash of the combined lines
|
|
26
|
+
*/
|
|
27
|
+
function hashLineSequence(lines) {
|
|
28
|
+
return (0, hashing_1.fnv1a)(lines.join('\n'));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Find block boundaries using line content patterns
|
|
32
|
+
* Blocks are natural groupings where we see clear separators
|
|
33
|
+
* @param lines - Array of LineInfo
|
|
34
|
+
* @returns Array of boundary indices
|
|
35
|
+
*/
|
|
36
|
+
function findBlockBoundaries(lines) {
|
|
37
|
+
const boundaries = [0]; // Start is always a boundary
|
|
38
|
+
// Common block separator patterns
|
|
39
|
+
const separatorPatterns = [
|
|
40
|
+
/^={3,}/, // ===...
|
|
41
|
+
/^-{3,}/, // ---...
|
|
42
|
+
/^#{3,}/, // ###...
|
|
43
|
+
/^\*{3,}/, // ***...
|
|
44
|
+
/^~{3,}/, // ~~~...
|
|
45
|
+
/^$/, // Empty line
|
|
46
|
+
/^(Test|Describe|Context|Suite|PASS|FAIL)[\s:]/i, // Test markers
|
|
47
|
+
/^\[?(INFO|ERROR|WARN|DEBUG)\]?[\s:]/i, // Log level changes
|
|
48
|
+
];
|
|
49
|
+
for (let i = 1; i < lines.length; i++) {
|
|
50
|
+
const text = lines[i].text.trim();
|
|
51
|
+
// Check if line matches any separator pattern
|
|
52
|
+
const isSeparator = separatorPatterns.some((p) => p.test(text));
|
|
53
|
+
// Also check for significant content changes (header-like lines)
|
|
54
|
+
const isHeader = /^={2,}\s*\w/.test(text) || // === Something ===
|
|
55
|
+
/^#{1,3}\s/.test(text); // # Header
|
|
56
|
+
if (isSeparator || isHeader) {
|
|
57
|
+
boundaries.push(i);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
boundaries.push(lines.length); // End is always a boundary
|
|
61
|
+
return [...new Set(boundaries)].sort((a, b) => a - b);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Extract blocks between boundaries and identify repeats
|
|
65
|
+
* @param lines - Array of LineInfo
|
|
66
|
+
* @param boundaries - Block boundary indices
|
|
67
|
+
* @param minBlockSize - Minimum lines to consider as block
|
|
68
|
+
* @returns Map of block hash to block info
|
|
69
|
+
*/
|
|
70
|
+
function extractBlocks(lines, boundaries, minBlockSize) {
|
|
71
|
+
const blockMap = new Map();
|
|
72
|
+
for (let i = 0; i < boundaries.length - 1; i++) {
|
|
73
|
+
const start = boundaries[i];
|
|
74
|
+
const end = boundaries[i + 1];
|
|
75
|
+
const blockLines = lines.slice(start, end).map((l) => l.text);
|
|
76
|
+
// Skip blocks that are too small
|
|
77
|
+
if (blockLines.length < minBlockSize) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const hash = hashLineSequence(blockLines);
|
|
81
|
+
const existing = blockMap.get(hash);
|
|
82
|
+
if (existing) {
|
|
83
|
+
existing.indices.push(start);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
blockMap.set(hash, { lines: blockLines, indices: [start] });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return blockMap;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Find repeated subsequences within the lines using sliding window
|
|
93
|
+
* @param lines - Array of LineInfo
|
|
94
|
+
* @param minBlockSize - Minimum block size
|
|
95
|
+
* @returns Map of hash to block info
|
|
96
|
+
*/
|
|
97
|
+
function findRepeatedSubsequences(lines, minBlockSize) {
|
|
98
|
+
const blockMap = new Map();
|
|
99
|
+
// Try different window sizes from minBlockSize up to a reasonable max
|
|
100
|
+
const maxWindowSize = Math.min(20, Math.floor(lines.length / 2));
|
|
101
|
+
for (let windowSize = minBlockSize; windowSize <= maxWindowSize; windowSize++) {
|
|
102
|
+
for (let i = 0; i <= lines.length - windowSize; i++) {
|
|
103
|
+
const windowLines = lines.slice(i, i + windowSize).map((l) => l.text);
|
|
104
|
+
const hash = hashLineSequence(windowLines);
|
|
105
|
+
const existing = blockMap.get(hash);
|
|
106
|
+
if (existing) {
|
|
107
|
+
// Check if this is a new occurrence (not overlapping)
|
|
108
|
+
const lastIndex = existing.indices[existing.indices.length - 1];
|
|
109
|
+
if (i >= lastIndex + existing.lines.length) {
|
|
110
|
+
existing.indices.push(i);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
blockMap.set(hash, { lines: windowLines, indices: [i] });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return blockMap;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Detect repeated blocks of lines
|
|
122
|
+
* @param lines - Array of LineInfo
|
|
123
|
+
* @param options - Compressor options
|
|
124
|
+
* @returns BlockResult with detected blocks
|
|
125
|
+
*/
|
|
126
|
+
function detectBlocks(lines, options) {
|
|
127
|
+
const { minBlockSize } = options;
|
|
128
|
+
// First try natural boundary detection
|
|
129
|
+
const boundaries = findBlockBoundaries(lines);
|
|
130
|
+
let blockMap = extractBlocks(lines, boundaries, minBlockSize);
|
|
131
|
+
// Also look for repeated subsequences that might not align with boundaries
|
|
132
|
+
const subseqBlocks = findRepeatedSubsequences(lines, minBlockSize);
|
|
133
|
+
// Merge results, preferring longer blocks
|
|
134
|
+
for (const [hash, info] of subseqBlocks) {
|
|
135
|
+
if (info.indices.length > 1) {
|
|
136
|
+
const existing = blockMap.get(hash);
|
|
137
|
+
if (!existing || info.lines.length > existing.lines.length) {
|
|
138
|
+
blockMap.set(hash, info);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Filter to only blocks that appear more than once
|
|
143
|
+
const repeatedBlocks = [];
|
|
144
|
+
let blockId = 1;
|
|
145
|
+
const coveredIndices = new Set();
|
|
146
|
+
// Sort by occurrence count descending, then by size descending
|
|
147
|
+
const sortedBlocks = [...blockMap.entries()]
|
|
148
|
+
.filter(([_, info]) => info.indices.length > 1)
|
|
149
|
+
.sort((a, b) => {
|
|
150
|
+
const countDiff = b[1].indices.length - a[1].indices.length;
|
|
151
|
+
if (countDiff !== 0)
|
|
152
|
+
return countDiff;
|
|
153
|
+
return b[1].lines.length - a[1].lines.length;
|
|
154
|
+
});
|
|
155
|
+
for (const [hash, info] of sortedBlocks) {
|
|
156
|
+
// Check if any of these indices are already covered
|
|
157
|
+
const uncoveredIndices = info.indices.filter((idx) => {
|
|
158
|
+
for (let i = 0; i < info.lines.length; i++) {
|
|
159
|
+
if (coveredIndices.has(idx + i)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
165
|
+
if (uncoveredIndices.length > 1) {
|
|
166
|
+
const block = {
|
|
167
|
+
id: `B${blockId++}`,
|
|
168
|
+
startIndex: uncoveredIndices[0],
|
|
169
|
+
lines: info.lines,
|
|
170
|
+
hash,
|
|
171
|
+
occurrences: uncoveredIndices.length,
|
|
172
|
+
indices: uncoveredIndices,
|
|
173
|
+
};
|
|
174
|
+
repeatedBlocks.push(block);
|
|
175
|
+
// Mark these indices as covered
|
|
176
|
+
for (const idx of uncoveredIndices) {
|
|
177
|
+
for (let i = 0; i < info.lines.length; i++) {
|
|
178
|
+
coveredIndices.add(idx + i);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Calculate remaining indices
|
|
184
|
+
const remainingIndices = new Set();
|
|
185
|
+
for (let i = 0; i < lines.length; i++) {
|
|
186
|
+
if (!coveredIndices.has(i)) {
|
|
187
|
+
remainingIndices.add(i);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
blocks: repeatedBlocks,
|
|
192
|
+
remainingIndices,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get block coverage info for debugging
|
|
197
|
+
* @param blocks - Detected blocks
|
|
198
|
+
* @param totalLines - Total number of lines
|
|
199
|
+
* @returns Coverage statistics
|
|
200
|
+
*/
|
|
201
|
+
function getBlockCoverage(blocks, totalLines) {
|
|
202
|
+
const covered = new Set();
|
|
203
|
+
for (const block of blocks) {
|
|
204
|
+
for (const idx of block.indices) {
|
|
205
|
+
for (let i = 0; i < block.lines.length; i++) {
|
|
206
|
+
covered.add(idx + i);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const coveredLines = covered.size;
|
|
211
|
+
const percentage = ((coveredLines / totalLines) * 100).toFixed(1);
|
|
212
|
+
return { coveredLines, percentage: `${percentage}%` };
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main compression orchestrator
|
|
3
|
+
* Coordinates all compression phases and produces final output
|
|
4
|
+
*/
|
|
5
|
+
import { CompressorOptions } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Parse input text into lines
|
|
8
|
+
* Handles different line endings
|
|
9
|
+
*/
|
|
10
|
+
declare function parseLines(input: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Main compression function
|
|
13
|
+
* @param input - Raw input text
|
|
14
|
+
* @param options - Compression options (partial, merged with defaults)
|
|
15
|
+
* @returns Compressed output string
|
|
16
|
+
*/
|
|
17
|
+
export declare function compress(input: string, options?: Partial<CompressorOptions>): string;
|
|
18
|
+
/**
|
|
19
|
+
* Compress with verbose output for debugging
|
|
20
|
+
*/
|
|
21
|
+
export declare function compressVerbose(input: string, options?: Partial<CompressorOptions>): {
|
|
22
|
+
output: string;
|
|
23
|
+
stats: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
export { parseLines };
|
|
26
|
+
//# sourceMappingURL=compressor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressor.d.ts","sourceRoot":"","sources":["../src/compressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,iBAAiB,EASlB,MAAM,SAAS,CAAC;AAMjB;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG3C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACvC,MAAM,CAgCR;AA4JD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACvC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAkBpD;AAGD,OAAO,EAAE,UAAU,EAAE,CAAC"}
|