@andrew_l/binlog 0.2.15 → 0.2.17

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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Binlog
2
2
 
3
- ![license](https://img.shields.io/npm/l/%40andrew_l%2Fbinlog) <!-- omit in toc -->
4
- ![npm version](https://img.shields.io/npm/v/%40andrew_l%2Fbinlog) <!-- omit in toc -->
5
- ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40andrew_l%2Fbinlog) <!-- omit in toc -->
3
+ ![license](https://img.shields.io/npm/l/%40andrew_l%2Fbinlog)
4
+ ![npm version](https://img.shields.io/npm/v/%40andrew_l%2Fbinlog)
5
+ ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40andrew_l%2Fbinlog)
6
6
 
7
7
  A high-performance binary logging system for Node.js applications, inspired by VK's KPHP KDB binlog implementation. This library provides a robust, efficient way to record and replay binary log entries with integrity checking and rotation support.
8
8
 
package/dist/index.cjs CHANGED
@@ -30,7 +30,10 @@ class Binlog {
30
30
  });
31
31
  constructor(options) {
32
32
  this._log = toolkit.logger("Binlog");
33
- this._options = toolkit.deepClone(options);
33
+ this._options = {
34
+ ...options,
35
+ structures: options.structures ? [...options.structures] : []
36
+ };
34
37
  this._filePattern = path__default.basename(options.path);
35
38
  this._directory = path__default.dirname(options.path);
36
39
  this._currentFile = null;
@@ -105,8 +108,13 @@ class Binlog {
105
108
  toolkit.assert.ok(this._currentFileDescriptor, "File descriptor is null");
106
109
  let flags = this.BINLOG_FLAG.NONE;
107
110
  if (!Buffer.isBuffer(data)) {
108
- data = tlPack.tlEncode(data);
109
- flags |= this.BINLOG_FLAG.TL;
111
+ if (data instanceof tlPack.Structure) {
112
+ data = tlPack.tlEncode(data);
113
+ flags |= this.BINLOG_FLAG.TL;
114
+ } else {
115
+ data = tlPack.tlEncode(data);
116
+ flags |= this.BINLOG_FLAG.TL;
117
+ }
110
118
  }
111
119
  const dataLength = data.length;
112
120
  const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;
@@ -190,7 +198,9 @@ class Binlog {
190
198
  const calculatedCrc = toolkit.crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;
191
199
  if (calculatedCrc === crc) {
192
200
  if (toolkit.checkBitmask(flags, this.BINLOG_FLAG.TL)) {
193
- data = tlPack.tlDecode(data);
201
+ data = tlPack.tlDecode(data, {
202
+ structures: this._options.structures
203
+ });
194
204
  }
195
205
  entries.push({
196
206
  opcode,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/Binlog.ts","../src/index.ts"],"sourcesContent":["import { tlDecode, tlEncode } from '@andrew_l/tl-pack';\nimport {\n type Logger,\n assert,\n checkBitmask,\n crc32,\n deepClone,\n logger,\n timestamp,\n} from '@andrew_l/toolkit';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Options for configuring the KdbBinlog\n */\nexport interface BinlogOptions {\n /**\n * File location to store binlog files\n * @default './binlogs/log-{index}.bin'\n */\n path: string;\n\n /**\n * Maximum size of each binlog file before rotation (in bytes)\n * @default 104857600\n */\n maxFileSize: number;\n\n /**\n * Whether to enable log rotation\n * @default true\n */\n rotation: boolean;\n\n /**\n * Whether to sync writes to disk immediately\n * @default true\n */\n syncWrites: boolean;\n}\n\n/**\n * Represents a single binlog entry\n */\nexport interface BinlogEntry<TData = Buffer> {\n /**\n * Operation code defining the type of operation\n */\n opcode: number;\n\n /**\n * Unix timestamp when the entry was created\n */\n timestamp: number;\n\n /**\n * Binary data of the entry\n */\n data: TData | Buffer;\n\n /**\n * Position in the file where this entry starts\n */\n position: number;\n}\n\n/**\n * TypeScript implementation of binlog system\n * Adapted from https://github.com/vk-com/kphp-kdb/blob/master/binlog/kdb-binlog-common.c\n * @group Main\n */\nexport class Binlog {\n private _options: Required<BinlogOptions>;\n private _filePattern: string;\n private _directory: string;\n private _currentFile: string | null;\n private _currentFileDescriptor: fs.promises.FileHandle | null;\n private _currentFileSize: number;\n private _currentFileIndex: number;\n private _isOpen: boolean;\n private _log: Logger;\n\n private readonly BINLOG_MAGIC: number = 0x4442544b;\n private readonly BINLOG_VERSION: number = 1;\n private readonly BINLOG_HEADER_SIZE: number = 16;\n private readonly BINLOG_ENTRY_HEADER_SIZE: number = 20;\n private readonly BINLOG_FLAG = Object.freeze({\n NONE: 0,\n TL: 1 << 0,\n } as const);\n\n constructor(options: BinlogOptions) {\n this._log = logger('Binlog');\n this._options = deepClone(options);\n this._filePattern = path.basename(options.path);\n this._directory = path.dirname(options.path);\n this._currentFile = null;\n this._currentFileDescriptor = null;\n this._currentFileSize = 0;\n this._currentFileIndex = 0;\n this._isOpen = false;\n }\n\n /**\n * Initialize the binlog system, ensuring directory exists and pick most recent binlog file\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async init(): Promise<void> {\n await fs.promises.mkdir(this._directory, { recursive: true });\n await this.#findLatestBinlogFile();\n }\n\n /**\n * Find the latest binlog file to continue writing\n */\n async #findLatestBinlogFile(): Promise<void> {\n const files = await fs.promises.readdir(this._directory);\n const fileRegEx = filePatternToRegex(this._filePattern);\n\n this._currentFileIndex = 0;\n\n for (const fileName of files) {\n const match = fileName.match(fileRegEx);\n\n if (match) {\n this._currentFileIndex = Math.max(\n this._currentFileIndex,\n parseInt(match[1], 10),\n );\n }\n }\n }\n\n /**\n * Open the binlog for writing\n */\n public async open(): Promise<void> {\n const filePath = path.join(this._directory, this.currentFileName);\n\n this._currentFileDescriptor = await fs.promises.open(filePath, 'a+');\n this._currentFile = filePath;\n this._currentFileSize = (await this._currentFileDescriptor.stat()).size;\n this._isOpen = true;\n\n // Write the binlog header\n if (this._currentFileSize === 0) {\n await this.#writeBinlogHeader();\n } else {\n await this.#checkBinlogHeaders(this._currentFileDescriptor);\n }\n }\n\n /**\n * Close the current binlog file\n */\n public async close(): Promise<void> {\n if (!this._isOpen || !this._currentFileDescriptor) return;\n\n // Write footer\n await this._currentFileDescriptor.close();\n\n this._currentFileDescriptor = null;\n this._currentFile = null;\n this._isOpen = false;\n }\n\n /**\n * Write a binlog entry\n * @param opcode - Operation code\n * @param data - Data to write\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async write(opcode: number, data: unknown): Promise<void> {\n if (!this._isOpen) {\n await this.open();\n }\n\n // Check if we need to rotate the file\n if (\n this._options.rotation &&\n this._currentFileSize >= this._options.maxFileSize\n ) {\n await this.rotate();\n }\n\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n let flags = this.BINLOG_FLAG.NONE;\n\n if (!Buffer.isBuffer(data)) {\n data = tlEncode(data) as Buffer;\n flags |= this.BINLOG_FLAG.TL;\n }\n\n // Create entry header\n const dataLength = (data as Buffer).length;\n const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_ENTRY_HEADER_SIZE);\n headerBuffer.writeUInt32LE(opcode, 0);\n headerBuffer.writeUInt32LE(flags, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(dataLength, 12);\n\n headerBuffer.writeUInt32LE(\n crc32(Buffer.concat([headerBuffer.subarray(0, 16), data as Buffer])) >>>\n 0,\n 16,\n );\n\n // Write the entry\n await this._currentFileDescriptor.write(headerBuffer);\n await this._currentFileDescriptor.write(data as Buffer);\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n\n this._currentFileSize += totalLength;\n }\n\n /**\n * Rotate the binlog file\n */\n public async rotate(): Promise<void> {\n await this.close();\n this._currentFileIndex++;\n await this.open();\n }\n\n /**\n * Write the binlog header\n */\n async #writeBinlogHeader(): Promise<void> {\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n\n headerBuffer.writeUInt32LE(this.BINLOG_MAGIC, 0);\n headerBuffer.writeUInt32LE(this.BINLOG_VERSION, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(0, 12); // Reserved field\n\n await this._currentFileDescriptor.write(headerBuffer);\n this._currentFileSize += headerBuffer.length;\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n }\n\n /**\n * Read and parse all entries from a binlog file\n * @param filename - Binlog file to read\n * @param unsafe - Ignore broken binlog records otherwise throws error\n * @returns Array of parsed entries\n */\n public async readEntries<TData = Buffer>(\n filename: string,\n unsafe?: boolean,\n ): Promise<BinlogEntry<TData>[]> {\n const filepath = path.join(this._directory, filename);\n const fileHandle = await fs.promises.open(filepath, 'r');\n\n if (!(await this.#checkBinlogHeaders(fileHandle))) {\n if (unsafe) {\n this._log.warn('Invalid binlog file format: ' + filename);\n return [];\n }\n\n throw new Error('Invalid binlog file format:\\n' + filepath);\n }\n\n const contentSize =\n (await fileHandle.stat()).size - this.BINLOG_HEADER_SIZE;\n\n let position = this.BINLOG_HEADER_SIZE;\n\n const entries: BinlogEntry<TData>[] = [];\n\n // Read entries until we reach the end or footer\n while (position < contentSize) {\n const entryHeaderBuffer = Buffer.allocUnsafe(\n this.BINLOG_ENTRY_HEADER_SIZE,\n );\n await fileHandle.read(\n entryHeaderBuffer,\n 0,\n this.BINLOG_ENTRY_HEADER_SIZE,\n position,\n );\n\n const opcode = entryHeaderBuffer.readUInt32LE(0);\n const flags = entryHeaderBuffer.readUInt32LE(4);\n const timestamp = entryHeaderBuffer.readUInt32LE(8);\n const dataLength = entryHeaderBuffer.readUInt32LE(12);\n const crc = entryHeaderBuffer.readUInt32LE(16);\n\n position += this.BINLOG_ENTRY_HEADER_SIZE;\n\n // Read data\n let data = Buffer.allocUnsafe(dataLength);\n\n await fileHandle.read(data, 0, dataLength, position);\n\n // Verify CRC\n const calculatedCrc =\n crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;\n\n if (calculatedCrc === crc) {\n if (checkBitmask(flags, this.BINLOG_FLAG.TL)) {\n data = tlDecode(data);\n }\n\n entries.push({\n opcode,\n timestamp,\n data: data,\n position: position - this.BINLOG_ENTRY_HEADER_SIZE,\n });\n } else if (!unsafe) {\n throw new Error(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n } else {\n this._log.warn(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n }\n\n position += dataLength;\n }\n\n // Close file\n await fileHandle.close();\n\n return entries;\n }\n\n async #checkBinlogHeaders(\n fileHandle: fs.promises.FileHandle,\n ): Promise<boolean> {\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n await fileHandle.read(headerBuffer, 0, this.BINLOG_HEADER_SIZE, 0);\n\n const magic = headerBuffer.readUInt32LE(0);\n const version = headerBuffer.readUInt32LE(4);\n\n return magic === this.BINLOG_MAGIC && version === this.BINLOG_VERSION;\n }\n\n /**\n * Get the current binlog file index\n */\n get currentFileIndex(): number {\n return this._currentFileIndex;\n }\n\n /**\n * Get current binlog file name\n */\n get currentFileName(): string {\n return this._filePattern.replaceAll(\n '{index}',\n String(this._currentFileIndex),\n );\n }\n\n /**\n * Get current binlog directory\n */\n get directory(): string {\n return this._directory;\n }\n}\n\nfunction filePatternToRegex(value: string): RegExp {\n return new RegExp(value.replaceAll('{index}', '(\\\\d+)'));\n}\n","import { deepDefaults } from '@andrew_l/toolkit';\nimport { Binlog, type BinlogOptions } from './Binlog.js';\n\nexport * from './Binlog.js';\n\nconst DEF_OPTIONS: BinlogOptions = {\n path: './binlogs/log-{index}.bin',\n maxFileSize: 104857600,\n rotation: true,\n syncWrites: true,\n};\n\n/**\n * Create binlog instance\n * @group Main\n */\nexport function createBinlog(options: Partial<BinlogOptions>): Binlog {\n return new Binlog(deepDefaults(options, DEF_OPTIONS));\n}\n"],"names":["logger","deepClone","path","fs","assert","tlEncode","timestamp","crc32","checkBitmask","tlDecode","deepDefaults"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/Binlog.ts","../src/index.ts"],"sourcesContent":["import { Structure, tlDecode, tlEncode } from '@andrew_l/tl-pack';\nimport {\n type Logger,\n assert,\n checkBitmask,\n crc32,\n logger,\n timestamp,\n} from '@andrew_l/toolkit';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Options for configuring the KdbBinlog\n */\nexport interface BinlogOptions {\n /**\n * File location to store binlog files\n * @default './binlogs/log-{index}.bin'\n */\n path: string;\n\n /**\n * Maximum size of each binlog file before rotation (in bytes)\n * @default 104857600\n */\n maxFileSize: number;\n\n /**\n * Whether to enable log rotation\n * @default true\n */\n rotation: boolean;\n\n /**\n * Whether to sync writes to disk immediately\n * @default true\n */\n syncWrites: boolean;\n\n structures?: Structure.Constructor[];\n}\n\n/**\n * Represents a single binlog entry\n */\nexport interface BinlogEntry<TData = Buffer> {\n /**\n * Operation code defining the type of operation\n */\n opcode: number;\n\n /**\n * Unix timestamp when the entry was created\n */\n timestamp: number;\n\n /**\n * Binary data of the entry\n */\n data: TData | Buffer;\n\n /**\n * Position in the file where this entry starts\n */\n position: number;\n}\n\n/**\n * TypeScript implementation of binlog system\n * Adapted from https://github.com/vk-com/kphp-kdb/blob/master/binlog/kdb-binlog-common.c\n * @group Main\n */\nexport class Binlog {\n private _options: Required<BinlogOptions>;\n private _filePattern: string;\n private _directory: string;\n private _currentFile: string | null;\n private _currentFileDescriptor: fs.promises.FileHandle | null;\n private _currentFileSize: number;\n private _currentFileIndex: number;\n private _isOpen: boolean;\n private _log: Logger;\n\n private readonly BINLOG_MAGIC: number = 0x4442544b;\n private readonly BINLOG_VERSION: number = 1;\n private readonly BINLOG_HEADER_SIZE: number = 16;\n private readonly BINLOG_ENTRY_HEADER_SIZE: number = 20;\n private readonly BINLOG_FLAG = Object.freeze({\n NONE: 0,\n TL: 1 << 0,\n } as const);\n\n constructor(options: BinlogOptions) {\n this._log = logger('Binlog');\n this._options = {\n ...options,\n structures: options.structures ? [...options.structures] : [],\n };\n this._filePattern = path.basename(options.path);\n this._directory = path.dirname(options.path);\n this._currentFile = null;\n this._currentFileDescriptor = null;\n this._currentFileSize = 0;\n this._currentFileIndex = 0;\n this._isOpen = false;\n }\n\n /**\n * Initialize the binlog system, ensuring directory exists and pick most recent binlog file\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async init(): Promise<void> {\n await fs.promises.mkdir(this._directory, { recursive: true });\n await this.#findLatestBinlogFile();\n }\n\n /**\n * Find the latest binlog file to continue writing\n */\n async #findLatestBinlogFile(): Promise<void> {\n const files = await fs.promises.readdir(this._directory);\n const fileRegEx = filePatternToRegex(this._filePattern);\n\n this._currentFileIndex = 0;\n\n for (const fileName of files) {\n const match = fileName.match(fileRegEx);\n\n if (match) {\n this._currentFileIndex = Math.max(\n this._currentFileIndex,\n parseInt(match[1], 10),\n );\n }\n }\n }\n\n /**\n * Open the binlog for writing\n */\n public async open(): Promise<void> {\n const filePath = path.join(this._directory, this.currentFileName);\n\n this._currentFileDescriptor = await fs.promises.open(filePath, 'a+');\n this._currentFile = filePath;\n this._currentFileSize = (await this._currentFileDescriptor.stat()).size;\n this._isOpen = true;\n\n // Write the binlog header\n if (this._currentFileSize === 0) {\n await this.#writeBinlogHeader();\n } else {\n await this.#checkBinlogHeaders(this._currentFileDescriptor);\n }\n }\n\n /**\n * Close the current binlog file\n */\n public async close(): Promise<void> {\n if (!this._isOpen || !this._currentFileDescriptor) return;\n\n // Write footer\n await this._currentFileDescriptor.close();\n\n this._currentFileDescriptor = null;\n this._currentFile = null;\n this._isOpen = false;\n }\n\n /**\n * Write a binlog entry\n * @param opcode - Operation code\n * @param data - Data to write\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async write(opcode: number, data: unknown): Promise<void> {\n if (!this._isOpen) {\n await this.open();\n }\n\n // Check if we need to rotate the file\n if (\n this._options.rotation &&\n this._currentFileSize >= this._options.maxFileSize\n ) {\n await this.rotate();\n }\n\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n let flags = this.BINLOG_FLAG.NONE;\n\n if (!Buffer.isBuffer(data)) {\n if (data instanceof Structure) {\n data = tlEncode(data);\n flags |= this.BINLOG_FLAG.TL;\n } else {\n data = tlEncode(data) as Buffer;\n flags |= this.BINLOG_FLAG.TL;\n }\n }\n\n // Create entry header\n const dataLength = (data as Buffer).length;\n const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_ENTRY_HEADER_SIZE);\n headerBuffer.writeUInt32LE(opcode, 0);\n headerBuffer.writeUInt32LE(flags, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(dataLength, 12);\n\n headerBuffer.writeUInt32LE(\n crc32(Buffer.concat([headerBuffer.subarray(0, 16), data as Buffer])) >>>\n 0,\n 16,\n );\n\n // Write the entry\n await this._currentFileDescriptor.write(headerBuffer);\n await this._currentFileDescriptor.write(data as Buffer);\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n\n this._currentFileSize += totalLength;\n }\n\n /**\n * Rotate the binlog file\n */\n public async rotate(): Promise<void> {\n await this.close();\n this._currentFileIndex++;\n await this.open();\n }\n\n /**\n * Write the binlog header\n */\n async #writeBinlogHeader(): Promise<void> {\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n\n headerBuffer.writeUInt32LE(this.BINLOG_MAGIC, 0);\n headerBuffer.writeUInt32LE(this.BINLOG_VERSION, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(0, 12); // Reserved field\n\n await this._currentFileDescriptor.write(headerBuffer);\n this._currentFileSize += headerBuffer.length;\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n }\n\n /**\n * Read and parse all entries from a binlog file\n * @param filename - Binlog file to read\n * @param unsafe - Ignore broken binlog records otherwise throws error\n * @returns Array of parsed entries\n */\n public async readEntries<TData = Buffer>(\n filename: string,\n unsafe?: boolean,\n ): Promise<BinlogEntry<TData>[]> {\n const filepath = path.join(this._directory, filename);\n const fileHandle = await fs.promises.open(filepath, 'r');\n\n if (!(await this.#checkBinlogHeaders(fileHandle))) {\n if (unsafe) {\n this._log.warn('Invalid binlog file format: ' + filename);\n return [];\n }\n\n throw new Error('Invalid binlog file format:\\n' + filepath);\n }\n\n const contentSize =\n (await fileHandle.stat()).size - this.BINLOG_HEADER_SIZE;\n\n let position = this.BINLOG_HEADER_SIZE;\n\n const entries: BinlogEntry<TData>[] = [];\n\n // Read entries until we reach the end or footer\n while (position < contentSize) {\n const entryHeaderBuffer = Buffer.allocUnsafe(\n this.BINLOG_ENTRY_HEADER_SIZE,\n );\n await fileHandle.read(\n entryHeaderBuffer,\n 0,\n this.BINLOG_ENTRY_HEADER_SIZE,\n position,\n );\n\n const opcode = entryHeaderBuffer.readUInt32LE(0);\n const flags = entryHeaderBuffer.readUInt32LE(4);\n const timestamp = entryHeaderBuffer.readUInt32LE(8);\n const dataLength = entryHeaderBuffer.readUInt32LE(12);\n const crc = entryHeaderBuffer.readUInt32LE(16);\n\n position += this.BINLOG_ENTRY_HEADER_SIZE;\n\n // Read data\n let data = Buffer.allocUnsafe(dataLength);\n\n await fileHandle.read(data, 0, dataLength, position);\n\n // Verify CRC\n const calculatedCrc =\n crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;\n\n if (calculatedCrc === crc) {\n if (checkBitmask(flags, this.BINLOG_FLAG.TL)) {\n data = tlDecode(data, {\n structures: this._options.structures,\n });\n }\n\n entries.push({\n opcode,\n timestamp,\n data: data,\n position: position - this.BINLOG_ENTRY_HEADER_SIZE,\n });\n } else if (!unsafe) {\n throw new Error(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n } else {\n this._log.warn(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n }\n\n position += dataLength;\n }\n\n // Close file\n await fileHandle.close();\n\n return entries;\n }\n\n async #checkBinlogHeaders(\n fileHandle: fs.promises.FileHandle,\n ): Promise<boolean> {\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n await fileHandle.read(headerBuffer, 0, this.BINLOG_HEADER_SIZE, 0);\n\n const magic = headerBuffer.readUInt32LE(0);\n const version = headerBuffer.readUInt32LE(4);\n\n return magic === this.BINLOG_MAGIC && version === this.BINLOG_VERSION;\n }\n\n /**\n * Get the current binlog file index\n */\n get currentFileIndex(): number {\n return this._currentFileIndex;\n }\n\n /**\n * Get current binlog file name\n */\n get currentFileName(): string {\n return this._filePattern.replaceAll(\n '{index}',\n String(this._currentFileIndex),\n );\n }\n\n /**\n * Get current binlog directory\n */\n get directory(): string {\n return this._directory;\n }\n}\n\nfunction filePatternToRegex(value: string): RegExp {\n return new RegExp(value.replaceAll('{index}', '(\\\\d+)'));\n}\n","import { deepDefaults } from '@andrew_l/toolkit';\nimport { Binlog, type BinlogOptions } from './Binlog.js';\n\nexport * from './Binlog.js';\n\nconst DEF_OPTIONS: BinlogOptions = {\n path: './binlogs/log-{index}.bin',\n maxFileSize: 104857600,\n rotation: true,\n syncWrites: true,\n};\n\n/**\n * Create binlog instance\n * @group Main\n */\nexport function createBinlog(options: Partial<BinlogOptions>): Binlog {\n return new Binlog(deepDefaults(options, DEF_OPTIONS));\n}\n"],"names":["logger","path","fs","assert","Structure","tlEncode","timestamp","crc32","checkBitmask","tlDecode","deepDefaults"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Structure } from '@andrew_l/tl-pack';
2
+
1
3
  /**
2
4
  * Options for configuring the KdbBinlog
3
5
  */
@@ -22,6 +24,7 @@ interface BinlogOptions {
22
24
  * @default true
23
25
  */
24
26
  syncWrites: boolean;
27
+ structures?: Structure.Constructor[];
25
28
  }
26
29
  /**
27
30
  * Represents a single binlog entry
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Structure } from '@andrew_l/tl-pack';
2
+
1
3
  /**
2
4
  * Options for configuring the KdbBinlog
3
5
  */
@@ -22,6 +24,7 @@ interface BinlogOptions {
22
24
  * @default true
23
25
  */
24
26
  syncWrites: boolean;
27
+ structures?: Structure.Constructor[];
25
28
  }
26
29
  /**
27
30
  * Represents a single binlog entry
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Structure } from '@andrew_l/tl-pack';
2
+
1
3
  /**
2
4
  * Options for configuring the KdbBinlog
3
5
  */
@@ -22,6 +24,7 @@ interface BinlogOptions {
22
24
  * @default true
23
25
  */
24
26
  syncWrites: boolean;
27
+ structures?: Structure.Constructor[];
25
28
  }
26
29
  /**
27
30
  * Represents a single binlog entry
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { logger, deepClone, assert, timestamp, crc32, checkBitmask, deepDefaults } from '@andrew_l/toolkit';
2
- import { tlEncode, tlDecode } from '@andrew_l/tl-pack';
1
+ import { logger, assert, timestamp, crc32, checkBitmask, deepDefaults } from '@andrew_l/toolkit';
2
+ import { Structure, tlEncode, tlDecode } from '@andrew_l/tl-pack';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
 
@@ -23,7 +23,10 @@ class Binlog {
23
23
  });
24
24
  constructor(options) {
25
25
  this._log = logger("Binlog");
26
- this._options = deepClone(options);
26
+ this._options = {
27
+ ...options,
28
+ structures: options.structures ? [...options.structures] : []
29
+ };
27
30
  this._filePattern = path.basename(options.path);
28
31
  this._directory = path.dirname(options.path);
29
32
  this._currentFile = null;
@@ -98,8 +101,13 @@ class Binlog {
98
101
  assert.ok(this._currentFileDescriptor, "File descriptor is null");
99
102
  let flags = this.BINLOG_FLAG.NONE;
100
103
  if (!Buffer.isBuffer(data)) {
101
- data = tlEncode(data);
102
- flags |= this.BINLOG_FLAG.TL;
104
+ if (data instanceof Structure) {
105
+ data = tlEncode(data);
106
+ flags |= this.BINLOG_FLAG.TL;
107
+ } else {
108
+ data = tlEncode(data);
109
+ flags |= this.BINLOG_FLAG.TL;
110
+ }
103
111
  }
104
112
  const dataLength = data.length;
105
113
  const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;
@@ -183,7 +191,9 @@ class Binlog {
183
191
  const calculatedCrc = crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;
184
192
  if (calculatedCrc === crc) {
185
193
  if (checkBitmask(flags, this.BINLOG_FLAG.TL)) {
186
- data = tlDecode(data);
194
+ data = tlDecode(data, {
195
+ structures: this._options.structures
196
+ });
187
197
  }
188
198
  entries.push({
189
199
  opcode,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/Binlog.ts","../src/index.ts"],"sourcesContent":["import { tlDecode, tlEncode } from '@andrew_l/tl-pack';\nimport {\n type Logger,\n assert,\n checkBitmask,\n crc32,\n deepClone,\n logger,\n timestamp,\n} from '@andrew_l/toolkit';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Options for configuring the KdbBinlog\n */\nexport interface BinlogOptions {\n /**\n * File location to store binlog files\n * @default './binlogs/log-{index}.bin'\n */\n path: string;\n\n /**\n * Maximum size of each binlog file before rotation (in bytes)\n * @default 104857600\n */\n maxFileSize: number;\n\n /**\n * Whether to enable log rotation\n * @default true\n */\n rotation: boolean;\n\n /**\n * Whether to sync writes to disk immediately\n * @default true\n */\n syncWrites: boolean;\n}\n\n/**\n * Represents a single binlog entry\n */\nexport interface BinlogEntry<TData = Buffer> {\n /**\n * Operation code defining the type of operation\n */\n opcode: number;\n\n /**\n * Unix timestamp when the entry was created\n */\n timestamp: number;\n\n /**\n * Binary data of the entry\n */\n data: TData | Buffer;\n\n /**\n * Position in the file where this entry starts\n */\n position: number;\n}\n\n/**\n * TypeScript implementation of binlog system\n * Adapted from https://github.com/vk-com/kphp-kdb/blob/master/binlog/kdb-binlog-common.c\n * @group Main\n */\nexport class Binlog {\n private _options: Required<BinlogOptions>;\n private _filePattern: string;\n private _directory: string;\n private _currentFile: string | null;\n private _currentFileDescriptor: fs.promises.FileHandle | null;\n private _currentFileSize: number;\n private _currentFileIndex: number;\n private _isOpen: boolean;\n private _log: Logger;\n\n private readonly BINLOG_MAGIC: number = 0x4442544b;\n private readonly BINLOG_VERSION: number = 1;\n private readonly BINLOG_HEADER_SIZE: number = 16;\n private readonly BINLOG_ENTRY_HEADER_SIZE: number = 20;\n private readonly BINLOG_FLAG = Object.freeze({\n NONE: 0,\n TL: 1 << 0,\n } as const);\n\n constructor(options: BinlogOptions) {\n this._log = logger('Binlog');\n this._options = deepClone(options);\n this._filePattern = path.basename(options.path);\n this._directory = path.dirname(options.path);\n this._currentFile = null;\n this._currentFileDescriptor = null;\n this._currentFileSize = 0;\n this._currentFileIndex = 0;\n this._isOpen = false;\n }\n\n /**\n * Initialize the binlog system, ensuring directory exists and pick most recent binlog file\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async init(): Promise<void> {\n await fs.promises.mkdir(this._directory, { recursive: true });\n await this.#findLatestBinlogFile();\n }\n\n /**\n * Find the latest binlog file to continue writing\n */\n async #findLatestBinlogFile(): Promise<void> {\n const files = await fs.promises.readdir(this._directory);\n const fileRegEx = filePatternToRegex(this._filePattern);\n\n this._currentFileIndex = 0;\n\n for (const fileName of files) {\n const match = fileName.match(fileRegEx);\n\n if (match) {\n this._currentFileIndex = Math.max(\n this._currentFileIndex,\n parseInt(match[1], 10),\n );\n }\n }\n }\n\n /**\n * Open the binlog for writing\n */\n public async open(): Promise<void> {\n const filePath = path.join(this._directory, this.currentFileName);\n\n this._currentFileDescriptor = await fs.promises.open(filePath, 'a+');\n this._currentFile = filePath;\n this._currentFileSize = (await this._currentFileDescriptor.stat()).size;\n this._isOpen = true;\n\n // Write the binlog header\n if (this._currentFileSize === 0) {\n await this.#writeBinlogHeader();\n } else {\n await this.#checkBinlogHeaders(this._currentFileDescriptor);\n }\n }\n\n /**\n * Close the current binlog file\n */\n public async close(): Promise<void> {\n if (!this._isOpen || !this._currentFileDescriptor) return;\n\n // Write footer\n await this._currentFileDescriptor.close();\n\n this._currentFileDescriptor = null;\n this._currentFile = null;\n this._isOpen = false;\n }\n\n /**\n * Write a binlog entry\n * @param opcode - Operation code\n * @param data - Data to write\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async write(opcode: number, data: unknown): Promise<void> {\n if (!this._isOpen) {\n await this.open();\n }\n\n // Check if we need to rotate the file\n if (\n this._options.rotation &&\n this._currentFileSize >= this._options.maxFileSize\n ) {\n await this.rotate();\n }\n\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n let flags = this.BINLOG_FLAG.NONE;\n\n if (!Buffer.isBuffer(data)) {\n data = tlEncode(data) as Buffer;\n flags |= this.BINLOG_FLAG.TL;\n }\n\n // Create entry header\n const dataLength = (data as Buffer).length;\n const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_ENTRY_HEADER_SIZE);\n headerBuffer.writeUInt32LE(opcode, 0);\n headerBuffer.writeUInt32LE(flags, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(dataLength, 12);\n\n headerBuffer.writeUInt32LE(\n crc32(Buffer.concat([headerBuffer.subarray(0, 16), data as Buffer])) >>>\n 0,\n 16,\n );\n\n // Write the entry\n await this._currentFileDescriptor.write(headerBuffer);\n await this._currentFileDescriptor.write(data as Buffer);\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n\n this._currentFileSize += totalLength;\n }\n\n /**\n * Rotate the binlog file\n */\n public async rotate(): Promise<void> {\n await this.close();\n this._currentFileIndex++;\n await this.open();\n }\n\n /**\n * Write the binlog header\n */\n async #writeBinlogHeader(): Promise<void> {\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n\n headerBuffer.writeUInt32LE(this.BINLOG_MAGIC, 0);\n headerBuffer.writeUInt32LE(this.BINLOG_VERSION, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(0, 12); // Reserved field\n\n await this._currentFileDescriptor.write(headerBuffer);\n this._currentFileSize += headerBuffer.length;\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n }\n\n /**\n * Read and parse all entries from a binlog file\n * @param filename - Binlog file to read\n * @param unsafe - Ignore broken binlog records otherwise throws error\n * @returns Array of parsed entries\n */\n public async readEntries<TData = Buffer>(\n filename: string,\n unsafe?: boolean,\n ): Promise<BinlogEntry<TData>[]> {\n const filepath = path.join(this._directory, filename);\n const fileHandle = await fs.promises.open(filepath, 'r');\n\n if (!(await this.#checkBinlogHeaders(fileHandle))) {\n if (unsafe) {\n this._log.warn('Invalid binlog file format: ' + filename);\n return [];\n }\n\n throw new Error('Invalid binlog file format:\\n' + filepath);\n }\n\n const contentSize =\n (await fileHandle.stat()).size - this.BINLOG_HEADER_SIZE;\n\n let position = this.BINLOG_HEADER_SIZE;\n\n const entries: BinlogEntry<TData>[] = [];\n\n // Read entries until we reach the end or footer\n while (position < contentSize) {\n const entryHeaderBuffer = Buffer.allocUnsafe(\n this.BINLOG_ENTRY_HEADER_SIZE,\n );\n await fileHandle.read(\n entryHeaderBuffer,\n 0,\n this.BINLOG_ENTRY_HEADER_SIZE,\n position,\n );\n\n const opcode = entryHeaderBuffer.readUInt32LE(0);\n const flags = entryHeaderBuffer.readUInt32LE(4);\n const timestamp = entryHeaderBuffer.readUInt32LE(8);\n const dataLength = entryHeaderBuffer.readUInt32LE(12);\n const crc = entryHeaderBuffer.readUInt32LE(16);\n\n position += this.BINLOG_ENTRY_HEADER_SIZE;\n\n // Read data\n let data = Buffer.allocUnsafe(dataLength);\n\n await fileHandle.read(data, 0, dataLength, position);\n\n // Verify CRC\n const calculatedCrc =\n crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;\n\n if (calculatedCrc === crc) {\n if (checkBitmask(flags, this.BINLOG_FLAG.TL)) {\n data = tlDecode(data);\n }\n\n entries.push({\n opcode,\n timestamp,\n data: data,\n position: position - this.BINLOG_ENTRY_HEADER_SIZE,\n });\n } else if (!unsafe) {\n throw new Error(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n } else {\n this._log.warn(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n }\n\n position += dataLength;\n }\n\n // Close file\n await fileHandle.close();\n\n return entries;\n }\n\n async #checkBinlogHeaders(\n fileHandle: fs.promises.FileHandle,\n ): Promise<boolean> {\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n await fileHandle.read(headerBuffer, 0, this.BINLOG_HEADER_SIZE, 0);\n\n const magic = headerBuffer.readUInt32LE(0);\n const version = headerBuffer.readUInt32LE(4);\n\n return magic === this.BINLOG_MAGIC && version === this.BINLOG_VERSION;\n }\n\n /**\n * Get the current binlog file index\n */\n get currentFileIndex(): number {\n return this._currentFileIndex;\n }\n\n /**\n * Get current binlog file name\n */\n get currentFileName(): string {\n return this._filePattern.replaceAll(\n '{index}',\n String(this._currentFileIndex),\n );\n }\n\n /**\n * Get current binlog directory\n */\n get directory(): string {\n return this._directory;\n }\n}\n\nfunction filePatternToRegex(value: string): RegExp {\n return new RegExp(value.replaceAll('{index}', '(\\\\d+)'));\n}\n","import { deepDefaults } from '@andrew_l/toolkit';\nimport { Binlog, type BinlogOptions } from './Binlog.js';\n\nexport * from './Binlog.js';\n\nconst DEF_OPTIONS: BinlogOptions = {\n path: './binlogs/log-{index}.bin',\n maxFileSize: 104857600,\n rotation: true,\n syncWrites: true,\n};\n\n/**\n * Create binlog instance\n * @group Main\n */\nexport function createBinlog(options: Partial<BinlogOptions>): Binlog {\n return new Binlog(deepDefaults(options, DEF_OPTIONS));\n}\n"],"names":["timestamp"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/Binlog.ts","../src/index.ts"],"sourcesContent":["import { Structure, tlDecode, tlEncode } from '@andrew_l/tl-pack';\nimport {\n type Logger,\n assert,\n checkBitmask,\n crc32,\n logger,\n timestamp,\n} from '@andrew_l/toolkit';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Options for configuring the KdbBinlog\n */\nexport interface BinlogOptions {\n /**\n * File location to store binlog files\n * @default './binlogs/log-{index}.bin'\n */\n path: string;\n\n /**\n * Maximum size of each binlog file before rotation (in bytes)\n * @default 104857600\n */\n maxFileSize: number;\n\n /**\n * Whether to enable log rotation\n * @default true\n */\n rotation: boolean;\n\n /**\n * Whether to sync writes to disk immediately\n * @default true\n */\n syncWrites: boolean;\n\n structures?: Structure.Constructor[];\n}\n\n/**\n * Represents a single binlog entry\n */\nexport interface BinlogEntry<TData = Buffer> {\n /**\n * Operation code defining the type of operation\n */\n opcode: number;\n\n /**\n * Unix timestamp when the entry was created\n */\n timestamp: number;\n\n /**\n * Binary data of the entry\n */\n data: TData | Buffer;\n\n /**\n * Position in the file where this entry starts\n */\n position: number;\n}\n\n/**\n * TypeScript implementation of binlog system\n * Adapted from https://github.com/vk-com/kphp-kdb/blob/master/binlog/kdb-binlog-common.c\n * @group Main\n */\nexport class Binlog {\n private _options: Required<BinlogOptions>;\n private _filePattern: string;\n private _directory: string;\n private _currentFile: string | null;\n private _currentFileDescriptor: fs.promises.FileHandle | null;\n private _currentFileSize: number;\n private _currentFileIndex: number;\n private _isOpen: boolean;\n private _log: Logger;\n\n private readonly BINLOG_MAGIC: number = 0x4442544b;\n private readonly BINLOG_VERSION: number = 1;\n private readonly BINLOG_HEADER_SIZE: number = 16;\n private readonly BINLOG_ENTRY_HEADER_SIZE: number = 20;\n private readonly BINLOG_FLAG = Object.freeze({\n NONE: 0,\n TL: 1 << 0,\n } as const);\n\n constructor(options: BinlogOptions) {\n this._log = logger('Binlog');\n this._options = {\n ...options,\n structures: options.structures ? [...options.structures] : [],\n };\n this._filePattern = path.basename(options.path);\n this._directory = path.dirname(options.path);\n this._currentFile = null;\n this._currentFileDescriptor = null;\n this._currentFileSize = 0;\n this._currentFileIndex = 0;\n this._isOpen = false;\n }\n\n /**\n * Initialize the binlog system, ensuring directory exists and pick most recent binlog file\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async init(): Promise<void> {\n await fs.promises.mkdir(this._directory, { recursive: true });\n await this.#findLatestBinlogFile();\n }\n\n /**\n * Find the latest binlog file to continue writing\n */\n async #findLatestBinlogFile(): Promise<void> {\n const files = await fs.promises.readdir(this._directory);\n const fileRegEx = filePatternToRegex(this._filePattern);\n\n this._currentFileIndex = 0;\n\n for (const fileName of files) {\n const match = fileName.match(fileRegEx);\n\n if (match) {\n this._currentFileIndex = Math.max(\n this._currentFileIndex,\n parseInt(match[1], 10),\n );\n }\n }\n }\n\n /**\n * Open the binlog for writing\n */\n public async open(): Promise<void> {\n const filePath = path.join(this._directory, this.currentFileName);\n\n this._currentFileDescriptor = await fs.promises.open(filePath, 'a+');\n this._currentFile = filePath;\n this._currentFileSize = (await this._currentFileDescriptor.stat()).size;\n this._isOpen = true;\n\n // Write the binlog header\n if (this._currentFileSize === 0) {\n await this.#writeBinlogHeader();\n } else {\n await this.#checkBinlogHeaders(this._currentFileDescriptor);\n }\n }\n\n /**\n * Close the current binlog file\n */\n public async close(): Promise<void> {\n if (!this._isOpen || !this._currentFileDescriptor) return;\n\n // Write footer\n await this._currentFileDescriptor.close();\n\n this._currentFileDescriptor = null;\n this._currentFile = null;\n this._isOpen = false;\n }\n\n /**\n * Write a binlog entry\n * @param opcode - Operation code\n * @param data - Data to write\n * @returns Promise resolving to true if successful, false otherwise\n */\n public async write(opcode: number, data: unknown): Promise<void> {\n if (!this._isOpen) {\n await this.open();\n }\n\n // Check if we need to rotate the file\n if (\n this._options.rotation &&\n this._currentFileSize >= this._options.maxFileSize\n ) {\n await this.rotate();\n }\n\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n let flags = this.BINLOG_FLAG.NONE;\n\n if (!Buffer.isBuffer(data)) {\n if (data instanceof Structure) {\n data = tlEncode(data);\n flags |= this.BINLOG_FLAG.TL;\n } else {\n data = tlEncode(data) as Buffer;\n flags |= this.BINLOG_FLAG.TL;\n }\n }\n\n // Create entry header\n const dataLength = (data as Buffer).length;\n const totalLength = this.BINLOG_ENTRY_HEADER_SIZE + dataLength;\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_ENTRY_HEADER_SIZE);\n headerBuffer.writeUInt32LE(opcode, 0);\n headerBuffer.writeUInt32LE(flags, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(dataLength, 12);\n\n headerBuffer.writeUInt32LE(\n crc32(Buffer.concat([headerBuffer.subarray(0, 16), data as Buffer])) >>>\n 0,\n 16,\n );\n\n // Write the entry\n await this._currentFileDescriptor.write(headerBuffer);\n await this._currentFileDescriptor.write(data as Buffer);\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n\n this._currentFileSize += totalLength;\n }\n\n /**\n * Rotate the binlog file\n */\n public async rotate(): Promise<void> {\n await this.close();\n this._currentFileIndex++;\n await this.open();\n }\n\n /**\n * Write the binlog header\n */\n async #writeBinlogHeader(): Promise<void> {\n assert.ok(this._currentFileDescriptor, 'File descriptor is null');\n\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n\n headerBuffer.writeUInt32LE(this.BINLOG_MAGIC, 0);\n headerBuffer.writeUInt32LE(this.BINLOG_VERSION, 4);\n headerBuffer.writeUInt32LE(timestamp(), 8);\n headerBuffer.writeUInt32LE(0, 12); // Reserved field\n\n await this._currentFileDescriptor.write(headerBuffer);\n this._currentFileSize += headerBuffer.length;\n\n if (this._options.syncWrites) {\n await this._currentFileDescriptor.sync();\n }\n }\n\n /**\n * Read and parse all entries from a binlog file\n * @param filename - Binlog file to read\n * @param unsafe - Ignore broken binlog records otherwise throws error\n * @returns Array of parsed entries\n */\n public async readEntries<TData = Buffer>(\n filename: string,\n unsafe?: boolean,\n ): Promise<BinlogEntry<TData>[]> {\n const filepath = path.join(this._directory, filename);\n const fileHandle = await fs.promises.open(filepath, 'r');\n\n if (!(await this.#checkBinlogHeaders(fileHandle))) {\n if (unsafe) {\n this._log.warn('Invalid binlog file format: ' + filename);\n return [];\n }\n\n throw new Error('Invalid binlog file format:\\n' + filepath);\n }\n\n const contentSize =\n (await fileHandle.stat()).size - this.BINLOG_HEADER_SIZE;\n\n let position = this.BINLOG_HEADER_SIZE;\n\n const entries: BinlogEntry<TData>[] = [];\n\n // Read entries until we reach the end or footer\n while (position < contentSize) {\n const entryHeaderBuffer = Buffer.allocUnsafe(\n this.BINLOG_ENTRY_HEADER_SIZE,\n );\n await fileHandle.read(\n entryHeaderBuffer,\n 0,\n this.BINLOG_ENTRY_HEADER_SIZE,\n position,\n );\n\n const opcode = entryHeaderBuffer.readUInt32LE(0);\n const flags = entryHeaderBuffer.readUInt32LE(4);\n const timestamp = entryHeaderBuffer.readUInt32LE(8);\n const dataLength = entryHeaderBuffer.readUInt32LE(12);\n const crc = entryHeaderBuffer.readUInt32LE(16);\n\n position += this.BINLOG_ENTRY_HEADER_SIZE;\n\n // Read data\n let data = Buffer.allocUnsafe(dataLength);\n\n await fileHandle.read(data, 0, dataLength, position);\n\n // Verify CRC\n const calculatedCrc =\n crc32(Buffer.concat([entryHeaderBuffer.subarray(0, 16), data])) >>> 0;\n\n if (calculatedCrc === crc) {\n if (checkBitmask(flags, this.BINLOG_FLAG.TL)) {\n data = tlDecode(data, {\n structures: this._options.structures,\n });\n }\n\n entries.push({\n opcode,\n timestamp,\n data: data,\n position: position - this.BINLOG_ENTRY_HEADER_SIZE,\n });\n } else if (!unsafe) {\n throw new Error(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n } else {\n this._log.warn(\n `CRC mismatch at position ${position - this.BINLOG_ENTRY_HEADER_SIZE}`,\n );\n }\n\n position += dataLength;\n }\n\n // Close file\n await fileHandle.close();\n\n return entries;\n }\n\n async #checkBinlogHeaders(\n fileHandle: fs.promises.FileHandle,\n ): Promise<boolean> {\n const headerBuffer = Buffer.allocUnsafe(this.BINLOG_HEADER_SIZE);\n await fileHandle.read(headerBuffer, 0, this.BINLOG_HEADER_SIZE, 0);\n\n const magic = headerBuffer.readUInt32LE(0);\n const version = headerBuffer.readUInt32LE(4);\n\n return magic === this.BINLOG_MAGIC && version === this.BINLOG_VERSION;\n }\n\n /**\n * Get the current binlog file index\n */\n get currentFileIndex(): number {\n return this._currentFileIndex;\n }\n\n /**\n * Get current binlog file name\n */\n get currentFileName(): string {\n return this._filePattern.replaceAll(\n '{index}',\n String(this._currentFileIndex),\n );\n }\n\n /**\n * Get current binlog directory\n */\n get directory(): string {\n return this._directory;\n }\n}\n\nfunction filePatternToRegex(value: string): RegExp {\n return new RegExp(value.replaceAll('{index}', '(\\\\d+)'));\n}\n","import { deepDefaults } from '@andrew_l/toolkit';\nimport { Binlog, type BinlogOptions } from './Binlog.js';\n\nexport * from './Binlog.js';\n\nconst DEF_OPTIONS: BinlogOptions = {\n path: './binlogs/log-{index}.bin',\n maxFileSize: 104857600,\n rotation: true,\n syncWrites: true,\n};\n\n/**\n * Create binlog instance\n * @group Main\n */\nexport function createBinlog(options: Partial<BinlogOptions>): Binlog {\n return new Binlog(deepDefaults(options, DEF_OPTIONS));\n}\n"],"names":["timestamp"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@andrew_l/binlog",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
+ "description": "A high-performance binary logging system for Node.js applications.",
4
5
  "license": "MIT",
5
6
  "type": "module",
6
7
  "keywords": [
@@ -29,8 +30,8 @@
29
30
  "vitest": "^2.1.3"
30
31
  },
31
32
  "dependencies": {
32
- "@andrew_l/tl-pack": "0.2.15",
33
- "@andrew_l/toolkit": "0.2.15"
33
+ "@andrew_l/toolkit": "0.2.17",
34
+ "@andrew_l/tl-pack": "0.2.17"
34
35
  },
35
36
  "scripts": {
36
37
  "build": "unbuild",