@andrew_l/binlog 0.2.15 → 0.2.16
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/dist/index.cjs +14 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +16 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
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 =
|
|
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
|
|
109
|
-
|
|
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,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;AAyEO,MAAM,MAAO,CAAA;AAAA,EACV,QAAA,CAAA;AAAA,EACA,YAAA,CAAA;AAAA,EACA,UAAA,CAAA;AAAA,EACA,YAAA,CAAA;AAAA,EACA,sBAAA,CAAA;AAAA,EACA,gBAAA,CAAA;AAAA,EACA,iBAAA,CAAA;AAAA,EACA,OAAA,CAAA;AAAA,EACA,IAAA,CAAA;AAAA,EAES,YAAuB,GAAA,UAAA,CAAA;AAAA,EACvB,cAAyB,GAAA,CAAA,CAAA;AAAA,EACzB,kBAA6B,GAAA,EAAA,CAAA;AAAA,EAC7B,wBAAmC,GAAA,EAAA,CAAA;AAAA,EACnC,WAAA,GAAc,OAAO,MAAO,CAAA;AAAA,IAC3C,IAAM,EAAA,CAAA;AAAA,IACN,IAAI,CAAK,IAAA,CAAA;AAAA,GACD,CAAA,CAAA;AAAA,EAEV,YAAY,OAAwB,EAAA;AAClC,IAAK,IAAA,CAAA,IAAA,GAAOA,eAAO,QAAQ,CAAA,CAAA;AAC3B,IAAA,IAAA,CAAK,QAAW,GAAA;AAAA,MACd,GAAG,OAAA;AAAA,MACH,UAAA,EAAY,QAAQ,UAAa,GAAA,CAAC,GAAG,OAAQ,CAAA,UAAU,IAAI,EAAC;AAAA,KAC9D,CAAA;AACA,IAAA,IAAA,CAAK,YAAe,GAAAC,aAAA,CAAK,QAAS,CAAA,OAAA,CAAQ,IAAI,CAAA,CAAA;AAC9C,IAAA,IAAA,CAAK,UAAa,GAAAA,aAAA,CAAK,OAAQ,CAAA,OAAA,CAAQ,IAAI,CAAA,CAAA;AAC3C,IAAA,IAAA,CAAK,YAAe,GAAA,IAAA,CAAA;AACpB,IAAA,IAAA,CAAK,sBAAyB,GAAA,IAAA,CAAA;AAC9B,IAAA,IAAA,CAAK,gBAAmB,GAAA,CAAA,CAAA;AACxB,IAAA,IAAA,CAAK,iBAAoB,GAAA,CAAA,CAAA;AACzB,IAAA,IAAA,CAAK,OAAU,GAAA,KAAA,CAAA;AAAA,GACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,IAAsB,GAAA;AACjC,IAAM,MAAAC,WAAA,CAAG,SAAS,KAAM,CAAA,IAAA,CAAK,YAAY,EAAE,SAAA,EAAW,MAAM,CAAA,CAAA;AAC5D,IAAA,MAAM,KAAK,qBAAsB,EAAA,CAAA;AAAA,GACnC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAuC,GAAA;AAC3C,IAAA,MAAM,QAAQ,MAAMA,WAAA,CAAG,QAAS,CAAA,OAAA,CAAQ,KAAK,UAAU,CAAA,CAAA;AACvD,IAAM,MAAA,SAAA,GAAY,kBAAmB,CAAA,IAAA,CAAK,YAAY,CAAA,CAAA;AAEtD,IAAA,IAAA,CAAK,iBAAoB,GAAA,CAAA,CAAA;AAEzB,IAAA,KAAA,MAAW,YAAY,KAAO,EAAA;AAC5B,MAAM,MAAA,KAAA,GAAQ,QAAS,CAAA,KAAA,CAAM,SAAS,CAAA,CAAA;AAEtC,MAAA,IAAI,KAAO,EAAA;AACT,QAAA,IAAA,CAAK,oBAAoB,IAAK,CAAA,GAAA;AAAA,UAC5B,IAAK,CAAA,iBAAA;AAAA,UACL,QAAS,CAAA,KAAA,CAAM,CAAC,CAAA,EAAG,EAAE,CAAA;AAAA,SACvB,CAAA;AAAA,OACF;AAAA,KACF;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,IAAsB,GAAA;AACjC,IAAA,MAAM,WAAWD,aAAK,CAAA,IAAA,CAAK,IAAK,CAAA,UAAA,EAAY,KAAK,eAAe,CAAA,CAAA;AAEhE,IAAA,IAAA,CAAK,yBAAyB,MAAMC,WAAA,CAAG,QAAS,CAAA,IAAA,CAAK,UAAU,IAAI,CAAA,CAAA;AACnE,IAAA,IAAA,CAAK,YAAe,GAAA,QAAA,CAAA;AACpB,IAAA,IAAA,CAAK,gBAAoB,GAAA,CAAA,MAAM,IAAK,CAAA,sBAAA,CAAuB,MAAQ,EAAA,IAAA,CAAA;AACnE,IAAA,IAAA,CAAK,OAAU,GAAA,IAAA,CAAA;AAGf,IAAI,IAAA,IAAA,CAAK,qBAAqB,CAAG,EAAA;AAC/B,MAAA,MAAM,KAAK,kBAAmB,EAAA,CAAA;AAAA,KACzB,MAAA;AACL,MAAM,MAAA,IAAA,CAAK,mBAAoB,CAAA,IAAA,CAAK,sBAAsB,CAAA,CAAA;AAAA,KAC5D;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,KAAuB,GAAA;AAClC,IAAA,IAAI,CAAC,IAAA,CAAK,OAAW,IAAA,CAAC,KAAK,sBAAwB,EAAA,OAAA;AAGnD,IAAM,MAAA,IAAA,CAAK,uBAAuB,KAAM,EAAA,CAAA;AAExC,IAAA,IAAA,CAAK,sBAAyB,GAAA,IAAA,CAAA;AAC9B,IAAA,IAAA,CAAK,YAAe,GAAA,IAAA,CAAA;AACpB,IAAA,IAAA,CAAK,OAAU,GAAA,KAAA,CAAA;AAAA,GACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,KAAM,CAAA,MAAA,EAAgB,IAA8B,EAAA;AAC/D,IAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AACjB,MAAA,MAAM,KAAK,IAAK,EAAA,CAAA;AAAA,KAClB;AAGA,IAAA,IACE,KAAK,QAAS,CAAA,QAAA,IACd,KAAK,gBAAoB,IAAA,IAAA,CAAK,SAAS,WACvC,EAAA;AACA,MAAA,MAAM,KAAK,MAAO,EAAA,CAAA;AAAA,KACpB;AAEA,IAAOC,cAAA,CAAA,EAAA,CAAG,IAAK,CAAA,sBAAA,EAAwB,yBAAyB,CAAA,CAAA;AAEhE,IAAI,IAAA,KAAA,GAAQ,KAAK,WAAY,CAAA,IAAA,CAAA;AAE7B,IAAA,IAAI,CAAC,MAAA,CAAO,QAAS,CAAA,IAAI,CAAG,EAAA;AAC1B,MAAA,IAAI,gBAAgBC,gBAAW,EAAA;AAC7B,QAAA,IAAA,GAAOC,gBAAS,IAAI,CAAA,CAAA;AACpB,QAAA,KAAA,IAAS,KAAK,WAAY,CAAA,EAAA,CAAA;AAAA,OACrB,MAAA;AACL,QAAA,IAAA,GAAOA,gBAAS,IAAI,CAAA,CAAA;AACpB,QAAA,KAAA,IAAS,KAAK,WAAY,CAAA,EAAA,CAAA;AAAA,OAC5B;AAAA,KACF;AAGA,IAAA,MAAM,aAAc,IAAgB,CAAA,MAAA,CAAA;AACpC,IAAM,MAAA,WAAA,GAAc,KAAK,wBAA2B,GAAA,UAAA,CAAA;AAEpD,IAAA,MAAM,YAAe,GAAA,MAAA,CAAO,WAAY,CAAA,IAAA,CAAK,wBAAwB,CAAA,CAAA;AACrE,IAAa,YAAA,CAAA,aAAA,CAAc,QAAQ,CAAC,CAAA,CAAA;AACpC,IAAa,YAAA,CAAA,aAAA,CAAc,OAAO,CAAC,CAAA,CAAA;AACnC,IAAa,YAAA,CAAA,aAAA,CAAcC,iBAAU,EAAA,EAAG,CAAC,CAAA,CAAA;AACzC,IAAa,YAAA,CAAA,aAAA,CAAc,YAAY,EAAE,CAAA,CAAA;AAEzC,IAAa,YAAA,CAAA,aAAA;AAAA,MACXC,aAAM,CAAA,MAAA,CAAO,MAAO,CAAA,CAAC,YAAa,CAAA,QAAA,CAAS,CAAG,EAAA,EAAE,CAAG,EAAA,IAAc,CAAC,CAAC,CACjE,KAAA,CAAA;AAAA,MACF,EAAA;AAAA,KACF,CAAA;AAGA,IAAM,MAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,CAAM,YAAY,CAAA,CAAA;AACpD,IAAM,MAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,CAAM,IAAc,CAAA,CAAA;AAEtD,IAAI,IAAA,IAAA,CAAK,SAAS,UAAY,EAAA;AAC5B,MAAM,MAAA,IAAA,CAAK,uBAAuB,IAAK,EAAA,CAAA;AAAA,KACzC;AAEA,IAAA,IAAA,CAAK,gBAAoB,IAAA,WAAA,CAAA;AAAA,GAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,MAAwB,GAAA;AACnC,IAAA,MAAM,KAAK,KAAM,EAAA,CAAA;AACjB,IAAK,IAAA,CAAA,iBAAA,EAAA,CAAA;AACL,IAAA,MAAM,KAAK,IAAK,EAAA,CAAA;AAAA,GAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAoC,GAAA;AACxC,IAAOJ,cAAA,CAAA,EAAA,CAAG,IAAK,CAAA,sBAAA,EAAwB,yBAAyB,CAAA,CAAA;AAEhE,IAAA,MAAM,YAAe,GAAA,MAAA,CAAO,WAAY,CAAA,IAAA,CAAK,kBAAkB,CAAA,CAAA;AAE/D,IAAa,YAAA,CAAA,aAAA,CAAc,IAAK,CAAA,YAAA,EAAc,CAAC,CAAA,CAAA;AAC/C,IAAa,YAAA,CAAA,aAAA,CAAc,IAAK,CAAA,cAAA,EAAgB,CAAC,CAAA,CAAA;AACjD,IAAa,YAAA,CAAA,aAAA,CAAcG,iBAAU,EAAA,EAAG,CAAC,CAAA,CAAA;AACzC,IAAa,YAAA,CAAA,aAAA,CAAc,GAAG,EAAE,CAAA,CAAA;AAEhC,IAAM,MAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,CAAM,YAAY,CAAA,CAAA;AACpD,IAAA,IAAA,CAAK,oBAAoB,YAAa,CAAA,MAAA,CAAA;AAEtC,IAAI,IAAA,IAAA,CAAK,SAAS,UAAY,EAAA;AAC5B,MAAM,MAAA,IAAA,CAAK,uBAAuB,IAAK,EAAA,CAAA;AAAA,KACzC;AAAA,GACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,WACX,CAAA,QAAA,EACA,MAC+B,EAAA;AAC/B,IAAA,MAAM,QAAW,GAAAL,aAAA,CAAK,IAAK,CAAA,IAAA,CAAK,YAAY,QAAQ,CAAA,CAAA;AACpD,IAAA,MAAM,aAAa,MAAMC,WAAA,CAAG,QAAS,CAAA,IAAA,CAAK,UAAU,GAAG,CAAA,CAAA;AAEvD,IAAA,IAAI,CAAE,MAAM,IAAK,CAAA,mBAAA,CAAoB,UAAU,CAAI,EAAA;AACjD,MAAA,IAAI,MAAQ,EAAA;AACV,QAAK,IAAA,CAAA,IAAA,CAAK,IAAK,CAAA,8BAAA,GAAiC,QAAQ,CAAA,CAAA;AACxD,QAAA,OAAO,EAAC,CAAA;AAAA,OACV;AAEA,MAAM,MAAA,IAAI,KAAM,CAAA,+BAAA,GAAkC,QAAQ,CAAA,CAAA;AAAA,KAC5D;AAEA,IAAA,MAAM,eACH,MAAM,UAAA,CAAW,IAAK,EAAA,EAAG,OAAO,IAAK,CAAA,kBAAA,CAAA;AAExC,IAAA,IAAI,WAAW,IAAK,CAAA,kBAAA,CAAA;AAEpB,IAAA,MAAM,UAAgC,EAAC,CAAA;AAGvC,IAAA,OAAO,WAAW,WAAa,EAAA;AAC7B,MAAA,MAAM,oBAAoB,MAAO,CAAA,WAAA;AAAA,QAC/B,IAAK,CAAA,wBAAA;AAAA,OACP,CAAA;AACA,MAAA,MAAM,UAAW,CAAA,IAAA;AAAA,QACf,iBAAA;AAAA,QACA,CAAA;AAAA,QACA,IAAK,CAAA,wBAAA;AAAA,QACL,QAAA;AAAA,OACF,CAAA;AAEA,MAAM,MAAA,MAAA,GAAS,iBAAkB,CAAA,YAAA,CAAa,CAAC,CAAA,CAAA;AAC/C,MAAM,MAAA,KAAA,GAAQ,iBAAkB,CAAA,YAAA,CAAa,CAAC,CAAA,CAAA;AAC9C,MAAMI,MAAAA,UAAAA,GAAY,iBAAkB,CAAA,YAAA,CAAa,CAAC,CAAA,CAAA;AAClD,MAAM,MAAA,UAAA,GAAa,iBAAkB,CAAA,YAAA,CAAa,EAAE,CAAA,CAAA;AACpD,MAAM,MAAA,GAAA,GAAM,iBAAkB,CAAA,YAAA,CAAa,EAAE,CAAA,CAAA;AAE7C,MAAA,QAAA,IAAY,IAAK,CAAA,wBAAA,CAAA;AAGjB,MAAI,IAAA,IAAA,GAAO,MAAO,CAAA,WAAA,CAAY,UAAU,CAAA,CAAA;AAExC,MAAA,MAAM,UAAW,CAAA,IAAA,CAAK,IAAM,EAAA,CAAA,EAAG,YAAY,QAAQ,CAAA,CAAA;AAGnD,MAAA,MAAM,aACJ,GAAAC,aAAA,CAAM,MAAO,CAAA,MAAA,CAAO,CAAC,iBAAA,CAAkB,QAAS,CAAA,CAAA,EAAG,EAAE,CAAA,EAAG,IAAI,CAAC,CAAC,CAAM,KAAA,CAAA,CAAA;AAEtE,MAAA,IAAI,kBAAkB,GAAK,EAAA;AACzB,QAAA,IAAIC,oBAAa,CAAA,KAAA,EAAO,IAAK,CAAA,WAAA,CAAY,EAAE,CAAG,EAAA;AAC5C,UAAA,IAAA,GAAOC,gBAAS,IAAM,EAAA;AAAA,YACpB,UAAA,EAAY,KAAK,QAAS,CAAA,UAAA;AAAA,WAC3B,CAAA,CAAA;AAAA,SACH;AAEA,QAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,UACX,MAAA;AAAA,UACA,SAAAH,EAAAA,UAAAA;AAAA,UACA,IAAA;AAAA,UACA,QAAA,EAAU,WAAW,IAAK,CAAA,wBAAA;AAAA,SAC3B,CAAA,CAAA;AAAA,OACH,MAAA,IAAW,CAAC,MAAQ,EAAA;AAClB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,yBAAA,EAA4B,QAAW,GAAA,IAAA,CAAK,wBAAwB,CAAA,CAAA;AAAA,SACtE,CAAA;AAAA,OACK,MAAA;AACL,QAAA,IAAA,CAAK,IAAK,CAAA,IAAA;AAAA,UACR,CAAA,yBAAA,EAA4B,QAAW,GAAA,IAAA,CAAK,wBAAwB,CAAA,CAAA;AAAA,SACtE,CAAA;AAAA,OACF;AAEA,MAAY,QAAA,IAAA,UAAA,CAAA;AAAA,KACd;AAGA,IAAA,MAAM,WAAW,KAAM,EAAA,CAAA;AAEvB,IAAO,OAAA,OAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,oBACJ,UACkB,EAAA;AAClB,IAAA,MAAM,YAAe,GAAA,MAAA,CAAO,WAAY,CAAA,IAAA,CAAK,kBAAkB,CAAA,CAAA;AAC/D,IAAA,MAAM,WAAW,IAAK,CAAA,YAAA,EAAc,CAAG,EAAA,IAAA,CAAK,oBAAoB,CAAC,CAAA,CAAA;AAEjE,IAAM,MAAA,KAAA,GAAQ,YAAa,CAAA,YAAA,CAAa,CAAC,CAAA,CAAA;AACzC,IAAM,MAAA,OAAA,GAAU,YAAa,CAAA,YAAA,CAAa,CAAC,CAAA,CAAA;AAE3C,IAAA,OAAO,KAAU,KAAA,IAAA,CAAK,YAAgB,IAAA,OAAA,KAAY,IAAK,CAAA,cAAA,CAAA;AAAA,GACzD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,gBAA2B,GAAA;AAC7B,IAAA,OAAO,IAAK,CAAA,iBAAA,CAAA;AAAA,GACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,eAA0B,GAAA;AAC5B,IAAA,OAAO,KAAK,YAAa,CAAA,UAAA;AAAA,MACvB,SAAA;AAAA,MACA,MAAA,CAAO,KAAK,iBAAiB,CAAA;AAAA,KAC/B,CAAA;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAoB,GAAA;AACtB,IAAA,OAAO,IAAK,CAAA,UAAA,CAAA;AAAA,GACd;AACF,CAAA;AAEA,SAAS,mBAAmB,KAAuB,EAAA;AACjD,EAAA,OAAO,IAAI,MAAO,CAAA,KAAA,CAAM,UAAW,CAAA,SAAA,EAAW,QAAQ,CAAC,CAAA,CAAA;AACzD;;ACjYA,MAAM,WAA6B,GAAA;AAAA,EACjC,IAAM,EAAA,2BAAA;AAAA,EACN,WAAa,EAAA,SAAA;AAAA,EACb,QAAU,EAAA,IAAA;AAAA,EACV,UAAY,EAAA,IAAA;AACd,CAAA,CAAA;AAMO,SAAS,aAAa,OAAyC,EAAA;AACpE,EAAA,OAAO,IAAI,MAAA,CAAOI,oBAAa,CAAA,OAAA,EAAS,WAAW,CAAC,CAAA,CAAA;AACtD;;;;;"}
|
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,
|
|
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 =
|
|
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
|
|
102
|
-
|
|
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,
|
package/dist/index.mjs.map
CHANGED
|
@@ -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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrew_l/binlog",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"vitest": "^2.1.3"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@andrew_l/tl-pack": "0.2.
|
|
33
|
-
"@andrew_l/toolkit": "0.2.
|
|
32
|
+
"@andrew_l/tl-pack": "0.2.16",
|
|
33
|
+
"@andrew_l/toolkit": "0.2.16"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "unbuild",
|