boom-format 0.9.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 +127 -0
- package/README.md +1079 -0
- package/SPECIFICATION.md +404 -0
- package/deno.json +15 -0
- package/dist/boom.min.cjs +17 -0
- package/dist/boom.min.js +1 -0
- package/dist/boom.obf.js +1 -0
- package/dist/chunk-5PQH6SJJ.js +5148 -0
- package/dist/cli.cjs +4241 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.global.js +4249 -0
- package/dist/cli.js +313 -0
- package/dist/index.cjs +5211 -0
- package/dist/index.d.cts +779 -0
- package/dist/index.d.ts +779 -0
- package/dist/index.global.js +5172 -0
- package/dist/index.js +82 -0
- package/package.json +96 -0
- package/samples/README.md +114 -0
- package/samples/api-response.boom +0 -0
- package/samples/api-response.boom.txt +1 -0
- package/samples/api-response.json +19 -0
- package/samples/config.boom +0 -0
- package/samples/config.boom.txt +1 -0
- package/samples/config.json +22 -0
- package/samples/users.boom +0 -0
- package/samples/users.boom.txt +1 -0
- package/samples/users.json +31 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
decode,
|
|
4
|
+
encode,
|
|
5
|
+
parseText,
|
|
6
|
+
stringify
|
|
7
|
+
} from "./chunk-5PQH6SJJ.js";
|
|
8
|
+
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
var VERSION = "1.0.0";
|
|
13
|
+
var BOOM_MAGIC = [66, 79, 79, 77];
|
|
14
|
+
function printHelp() {
|
|
15
|
+
console.log(`
|
|
16
|
+
BOOM CLI v${VERSION} - Binary Object Optimised Markup
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
boom encode <input.json> [output] Encode JSON to BOOM format
|
|
20
|
+
boom decode <input.boom> [output.json] Decode BOOM to JSON
|
|
21
|
+
boom info <input.boom> Show info about a BOOM file
|
|
22
|
+
boom <input.json> Shorthand for encode
|
|
23
|
+
|
|
24
|
+
Output Formats:
|
|
25
|
+
--binary, -b Output BOOM binary format (default)
|
|
26
|
+
--text, -t Output BOOM text format (.boom.txt)
|
|
27
|
+
--json, -j Output JSON format (for decode)
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--help, -h Show this help message
|
|
31
|
+
--version, -v Show version
|
|
32
|
+
--pretty, -p Pretty-print output (JSON and text)
|
|
33
|
+
--dict, -d Use shared dictionary (default: true)
|
|
34
|
+
--no-dict Disable shared dictionary
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
boom encode data.json # Output: data.boom (binary)
|
|
38
|
+
boom encode data.json --text # Output: data.boom.txt (text)
|
|
39
|
+
boom encode data.json output.boom # Custom output file
|
|
40
|
+
boom decode data.boom # Output: data.json
|
|
41
|
+
boom decode data.boom --pretty # Pretty-printed JSON
|
|
42
|
+
boom decode data.boom.txt # Decode BOOM text
|
|
43
|
+
boom info data.boom # Show file info
|
|
44
|
+
cat data.json | boom encode - > out.boom # Stdin/stdout
|
|
45
|
+
cat data.boom | boom decode - # Decode from stdin
|
|
46
|
+
|
|
47
|
+
File Extensions:
|
|
48
|
+
.boom BOOM binary format
|
|
49
|
+
.boom.txt BOOM text format (human-readable)
|
|
50
|
+
.json JSON format
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
function printVersion() {
|
|
54
|
+
console.log(`boom v${VERSION}`);
|
|
55
|
+
}
|
|
56
|
+
function parseArgs(args) {
|
|
57
|
+
const options = {
|
|
58
|
+
command: "help",
|
|
59
|
+
input: null,
|
|
60
|
+
output: null,
|
|
61
|
+
pretty: false,
|
|
62
|
+
useDict: true,
|
|
63
|
+
format: "binary"
|
|
64
|
+
};
|
|
65
|
+
const positional = [];
|
|
66
|
+
for (let i = 0; i < args.length; i++) {
|
|
67
|
+
const arg = args[i];
|
|
68
|
+
if (arg === void 0) continue;
|
|
69
|
+
if (arg === "--help" || arg === "-h") {
|
|
70
|
+
options.command = "help";
|
|
71
|
+
return options;
|
|
72
|
+
}
|
|
73
|
+
if (arg === "--version" || arg === "-v") {
|
|
74
|
+
options.command = "version";
|
|
75
|
+
return options;
|
|
76
|
+
}
|
|
77
|
+
if (arg === "--pretty" || arg === "-p") {
|
|
78
|
+
options.pretty = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (arg === "--dict" || arg === "-d") {
|
|
82
|
+
options.useDict = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (arg === "--no-dict") {
|
|
86
|
+
options.useDict = false;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (arg === "--binary" || arg === "-b") {
|
|
90
|
+
options.format = "binary";
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (arg === "--text" || arg === "-t") {
|
|
94
|
+
options.format = "text";
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (arg === "--json" || arg === "-j") {
|
|
98
|
+
options.format = "json";
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (arg === "-") {
|
|
102
|
+
positional.push(arg);
|
|
103
|
+
} else if (arg.startsWith("-")) {
|
|
104
|
+
console.error(`Unknown option: ${arg}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
} else {
|
|
107
|
+
positional.push(arg);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (positional.length === 0) {
|
|
111
|
+
options.command = "help";
|
|
112
|
+
return options;
|
|
113
|
+
}
|
|
114
|
+
const cmd = positional[0];
|
|
115
|
+
if (!cmd) {
|
|
116
|
+
options.command = "help";
|
|
117
|
+
return options;
|
|
118
|
+
}
|
|
119
|
+
const cmdLower = cmd.toLowerCase();
|
|
120
|
+
if (cmdLower === "encode") {
|
|
121
|
+
options.command = "encode";
|
|
122
|
+
options.input = positional[1] ?? null;
|
|
123
|
+
options.output = positional[2] ?? null;
|
|
124
|
+
} else if (cmdLower === "decode") {
|
|
125
|
+
options.command = "decode";
|
|
126
|
+
options.input = positional[1] ?? null;
|
|
127
|
+
options.output = positional[2] ?? null;
|
|
128
|
+
} else if (cmdLower === "info") {
|
|
129
|
+
options.command = "info";
|
|
130
|
+
options.input = positional[1] ?? null;
|
|
131
|
+
} else if (cmdLower === "help") {
|
|
132
|
+
options.command = "help";
|
|
133
|
+
} else if (cmdLower === "version") {
|
|
134
|
+
options.command = "version";
|
|
135
|
+
} else {
|
|
136
|
+
const inputFile = positional[0];
|
|
137
|
+
if (inputFile) {
|
|
138
|
+
options.input = inputFile;
|
|
139
|
+
options.output = positional[1] ?? null;
|
|
140
|
+
if (inputFile.endsWith(".boom") || inputFile.endsWith(".boom.txt")) {
|
|
141
|
+
options.command = "decode";
|
|
142
|
+
} else {
|
|
143
|
+
options.command = "encode";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return options;
|
|
148
|
+
}
|
|
149
|
+
function readInput(inputPath) {
|
|
150
|
+
if (inputPath === "-") {
|
|
151
|
+
return fs.readFileSync(0);
|
|
152
|
+
}
|
|
153
|
+
return fs.readFileSync(inputPath);
|
|
154
|
+
}
|
|
155
|
+
function getOutputPath(inputPath, command, format) {
|
|
156
|
+
if (inputPath === "-") {
|
|
157
|
+
return "-";
|
|
158
|
+
}
|
|
159
|
+
let base;
|
|
160
|
+
const dir = path.dirname(inputPath);
|
|
161
|
+
if (inputPath.endsWith(".boom.txt")) {
|
|
162
|
+
base = path.basename(inputPath, ".boom.txt");
|
|
163
|
+
} else {
|
|
164
|
+
const ext = path.extname(inputPath);
|
|
165
|
+
base = path.basename(inputPath, ext);
|
|
166
|
+
}
|
|
167
|
+
if (command === "encode") {
|
|
168
|
+
if (format === "text") {
|
|
169
|
+
return path.join(dir, base + ".boom.txt");
|
|
170
|
+
}
|
|
171
|
+
return path.join(dir, base + ".boom");
|
|
172
|
+
} else {
|
|
173
|
+
return path.join(dir, base + ".json");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function writeOutput(outputPath, data) {
|
|
177
|
+
if (outputPath === "-") {
|
|
178
|
+
if (typeof data === "string") {
|
|
179
|
+
process.stdout.write(data);
|
|
180
|
+
} else {
|
|
181
|
+
process.stdout.write(data);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
fs.writeFileSync(outputPath, data);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function formatBytes(bytes) {
|
|
188
|
+
if (bytes < 1024) return bytes + " B";
|
|
189
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB";
|
|
190
|
+
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
|
|
191
|
+
}
|
|
192
|
+
async function runEncode(options) {
|
|
193
|
+
if (!options.input) {
|
|
194
|
+
console.error("Error: No input file specified");
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
const inputData = readInput(options.input);
|
|
198
|
+
const jsonData = JSON.parse(inputData.toString("utf-8"));
|
|
199
|
+
const outputPath = options.output || getOutputPath(options.input, "encode", options.format);
|
|
200
|
+
if (options.format === "text") {
|
|
201
|
+
const textData = stringify(jsonData, { compact: !options.pretty });
|
|
202
|
+
writeOutput(outputPath, textData + "\n");
|
|
203
|
+
if (outputPath !== "-") {
|
|
204
|
+
const savings = ((1 - textData.length / inputData.length) * 100).toFixed(1);
|
|
205
|
+
console.log(`Encoded: ${options.input} -> ${outputPath}`);
|
|
206
|
+
console.log(` JSON: ${formatBytes(inputData.length)}`);
|
|
207
|
+
console.log(` BOOM text: ${formatBytes(textData.length)} (${savings}% smaller)`);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
const boomData = encode(jsonData, { enableInterning: options.useDict });
|
|
211
|
+
writeOutput(outputPath, Buffer.from(boomData));
|
|
212
|
+
if (outputPath !== "-") {
|
|
213
|
+
const savings = ((1 - boomData.length / inputData.length) * 100).toFixed(1);
|
|
214
|
+
console.log(`Encoded: ${options.input} -> ${outputPath}`);
|
|
215
|
+
console.log(` JSON: ${formatBytes(inputData.length)}`);
|
|
216
|
+
console.log(` BOOM binary: ${formatBytes(boomData.length)} (${savings}% smaller)`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function runDecode(options) {
|
|
221
|
+
if (!options.input) {
|
|
222
|
+
console.error("Error: No input file specified");
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
const inputData = readInput(options.input);
|
|
226
|
+
const isTextFormat = options.input.endsWith(".boom.txt") || options.input.endsWith(".txt") || !isBinaryFormat(inputData);
|
|
227
|
+
let jsonData;
|
|
228
|
+
let inputFormat;
|
|
229
|
+
if (isTextFormat) {
|
|
230
|
+
const textContent = inputData.toString("utf-8");
|
|
231
|
+
jsonData = parseText(textContent);
|
|
232
|
+
inputFormat = "BOOM text";
|
|
233
|
+
} else {
|
|
234
|
+
const boomArray = new Uint8Array(inputData);
|
|
235
|
+
if (boomArray.length < 6 || boomArray[0] !== BOOM_MAGIC[0] || boomArray[1] !== BOOM_MAGIC[1] || boomArray[2] !== BOOM_MAGIC[2] || boomArray[3] !== BOOM_MAGIC[3]) {
|
|
236
|
+
console.error("Error: Not a valid BOOM file (invalid magic header)");
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
jsonData = decode(boomArray);
|
|
240
|
+
inputFormat = "BOOM binary";
|
|
241
|
+
}
|
|
242
|
+
const jsonString = options.pretty ? JSON.stringify(jsonData, null, 2) : JSON.stringify(jsonData);
|
|
243
|
+
const outputPath = options.output || getOutputPath(options.input, "decode", options.format);
|
|
244
|
+
writeOutput(outputPath, jsonString + "\n");
|
|
245
|
+
if (outputPath !== "-") {
|
|
246
|
+
console.log(`Decoded: ${options.input} -> ${outputPath}`);
|
|
247
|
+
console.log(` ${inputFormat}: ${formatBytes(inputData.length)}`);
|
|
248
|
+
console.log(` JSON: ${formatBytes(jsonString.length)}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function isBinaryFormat(data) {
|
|
252
|
+
return data.length >= 4 && data[0] === BOOM_MAGIC[0] && data[1] === BOOM_MAGIC[1] && data[2] === BOOM_MAGIC[2] && data[3] === BOOM_MAGIC[3];
|
|
253
|
+
}
|
|
254
|
+
async function runInfo(options) {
|
|
255
|
+
if (!options.input) {
|
|
256
|
+
console.error("Error: No input file specified");
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
const inputData = readInput(options.input);
|
|
260
|
+
const boomArray = new Uint8Array(inputData);
|
|
261
|
+
if (boomArray.length < 6 || boomArray[0] !== BOOM_MAGIC[0] || boomArray[1] !== BOOM_MAGIC[1] || boomArray[2] !== BOOM_MAGIC[2] || boomArray[3] !== BOOM_MAGIC[3]) {
|
|
262
|
+
console.error("Error: Not a valid BOOM file (invalid magic header)");
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
const version = boomArray[4];
|
|
266
|
+
const flags = boomArray[5];
|
|
267
|
+
const usesDict = flags !== void 0 ? (flags & 1) !== 0 : false;
|
|
268
|
+
const jsonData = decode(boomArray);
|
|
269
|
+
const jsonString = JSON.stringify(jsonData);
|
|
270
|
+
const prettyJson = JSON.stringify(jsonData, null, 2);
|
|
271
|
+
const savings = ((1 - boomArray.length / jsonString.length) * 100).toFixed(1);
|
|
272
|
+
console.log(`BOOM File Info: ${options.input}`);
|
|
273
|
+
console.log(`${"\u2500".repeat(50)}`);
|
|
274
|
+
console.log(` Format version: ${version}`);
|
|
275
|
+
console.log(` Uses dictionary: ${usesDict ? "yes" : "no"}`);
|
|
276
|
+
console.log(` BOOM size: ${formatBytes(boomArray.length)}`);
|
|
277
|
+
console.log(` JSON size: ${formatBytes(jsonString.length)}`);
|
|
278
|
+
console.log(` Size reduction: ${savings}%`);
|
|
279
|
+
console.log(`${"\u2500".repeat(50)}`);
|
|
280
|
+
console.log(`Preview (first 500 chars):`);
|
|
281
|
+
console.log(prettyJson.slice(0, 500) + (prettyJson.length > 500 ? "..." : ""));
|
|
282
|
+
}
|
|
283
|
+
async function main() {
|
|
284
|
+
const args = process.argv.slice(2);
|
|
285
|
+
const options = parseArgs(args);
|
|
286
|
+
try {
|
|
287
|
+
switch (options.command) {
|
|
288
|
+
case "help":
|
|
289
|
+
printHelp();
|
|
290
|
+
break;
|
|
291
|
+
case "version":
|
|
292
|
+
printVersion();
|
|
293
|
+
break;
|
|
294
|
+
case "encode":
|
|
295
|
+
await runEncode(options);
|
|
296
|
+
break;
|
|
297
|
+
case "decode":
|
|
298
|
+
await runDecode(options);
|
|
299
|
+
break;
|
|
300
|
+
case "info":
|
|
301
|
+
await runInfo(options);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (error instanceof Error) {
|
|
306
|
+
console.error(`Error: ${error.message}`);
|
|
307
|
+
} else {
|
|
308
|
+
console.error("An unknown error occurred");
|
|
309
|
+
}
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
main();
|