commit 1d13e9fc3ef39cd485c98023b2e6ddfced548bae Author: Eli-Class Date: Tue Jan 27 02:30:42 2026 +0000 Init indexed file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..862f83b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +build/ +dist/ +*.log +*.node +.DS_Store \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..64fdf96 --- /dev/null +++ b/.npmignore @@ -0,0 +1,6 @@ +node_modules/ +build/ +*.log +.DS_Store +test.ts +tsconfig.json \ No newline at end of file diff --git a/CUSTOM_SERIALIZER.md b/CUSTOM_SERIALIZER.md new file mode 100644 index 0000000..5d54f8d --- /dev/null +++ b/CUSTOM_SERIALIZER.md @@ -0,0 +1,396 @@ +가변 길이 배열이 포함된 데이터의 커스텀 바이너리 직렬화 방법을 보여드릴게요. + +```typescript +// 가변 배열 직렬화 예시 +import { createSerializer, DataWriter, DataReader } from './src/data-file/index.js'; + +// ============================================ +// 1. 단순 배열 (숫자 배열) +// ============================================ + +interface SensorReading { + sensorId: number; + values: number[]; // 가변 길이 +} + +const sensorSerializer = createSerializer( + (data) => { + // 4(sensorId) + 4(배열길이) + 8*N(values) + const buf = Buffer.alloc(4 + 4 + data.values.length * 8); + let offset = 0; + + buf.writeUInt32LE(data.sensorId, offset); offset += 4; + buf.writeUInt32LE(data.values.length, offset); offset += 4; + + for (const v of data.values) { + buf.writeDoubleLE(v, offset); offset += 8; + } + + return buf; + }, + (buf) => { + let offset = 0; + + const sensorId = buf.readUInt32LE(offset); offset += 4; + const len = buf.readUInt32LE(offset); offset += 4; + + const values: number[] = []; + for (let i = 0; i < len; i++) { + values.push(buf.readDoubleLE(offset)); offset += 8; + } + + return { sensorId, values }; + } +); + + +// ============================================ +// 2. 객체 배열 +// ============================================ + +interface OrderItem { + sku: string; + qty: number; + price: number; +} + +interface Order { + orderId: number; + items: OrderItem[]; // 가변 길이 객체 배열 + total: number; +} + +const orderSerializer = createSerializer( + (data) => { + // 각 item의 sku 길이를 먼저 계산 + const skuBuffers = data.items.map(item => Buffer.from(item.sku, 'utf8')); + const itemsSize = skuBuffers.reduce( + (sum, skuBuf, i) => sum + 4 + skuBuf.length + 4 + 8, // skuLen + sku + qty + price + 0 + ); + + // 4(orderId) + 4(items길이) + itemsSize + 8(total) + const buf = Buffer.alloc(4 + 4 + itemsSize + 8); + let offset = 0; + + buf.writeUInt32LE(data.orderId, offset); offset += 4; + buf.writeUInt32LE(data.items.length, offset); offset += 4; + + for (let i = 0; i < data.items.length; i++) { + const item = data.items[i]; + const skuBuf = skuBuffers[i]; + + buf.writeUInt32LE(skuBuf.length, offset); offset += 4; + skuBuf.copy(buf, offset); offset += skuBuf.length; + buf.writeUInt32LE(item.qty, offset); offset += 4; + buf.writeDoubleLE(item.price, offset); offset += 8; + } + + buf.writeDoubleLE(data.total, offset); + + return buf; + }, + (buf) => { + let offset = 0; + + const orderId = buf.readUInt32LE(offset); offset += 4; + const itemsLen = buf.readUInt32LE(offset); offset += 4; + + const items: OrderItem[] = []; + for (let i = 0; i < itemsLen; i++) { + const skuLen = buf.readUInt32LE(offset); offset += 4; + const sku = buf.toString('utf8', offset, offset + skuLen); offset += skuLen; + const qty = buf.readUInt32LE(offset); offset += 4; + const price = buf.readDoubleLE(offset); offset += 8; + + items.push({ sku, qty, price }); + } + + const total = buf.readDoubleLE(offset); + + return { orderId, items, total }; + } +); + + +// ============================================ +// 3. 다중 가변 배열 +// ============================================ + +interface TimeSeries { + id: number; + timestamps: bigint[]; // 가변 + values: number[]; // 가변 + tags: string[]; // 가변 +} + +const timeSeriesSerializer = createSerializer( + (data) => { + const tagBuffers = data.tags.map(t => Buffer.from(t, 'utf8')); + const tagsSize = tagBuffers.reduce((sum, b) => sum + 4 + b.length, 0); + + // 4(id) + 4(tsLen) + 8*N + 4(valLen) + 8*M + 4(tagLen) + tagsSize + const size = 4 + 4 + data.timestamps.length * 8 + 4 + data.values.length * 8 + 4 + tagsSize; + const buf = Buffer.alloc(size); + let offset = 0; + + // id + buf.writeUInt32LE(data.id, offset); offset += 4; + + // timestamps + buf.writeUInt32LE(data.timestamps.length, offset); offset += 4; + for (const ts of data.timestamps) { + buf.writeBigUInt64LE(ts, offset); offset += 8; + } + + // values + buf.writeUInt32LE(data.values.length, offset); offset += 4; + for (const v of data.values) { + buf.writeDoubleLE(v, offset); offset += 8; + } + + // tags + buf.writeUInt32LE(data.tags.length, offset); offset += 4; + for (const tagBuf of tagBuffers) { + buf.writeUInt32LE(tagBuf.length, offset); offset += 4; + tagBuf.copy(buf, offset); offset += tagBuf.length; + } + + return buf; + }, + (buf) => { + let offset = 0; + + const id = buf.readUInt32LE(offset); offset += 4; + + // timestamps + const tsLen = buf.readUInt32LE(offset); offset += 4; + const timestamps: bigint[] = []; + for (let i = 0; i < tsLen; i++) { + timestamps.push(buf.readBigUInt64LE(offset)); offset += 8; + } + + // values + const valLen = buf.readUInt32LE(offset); offset += 4; + const values: number[] = []; + for (let i = 0; i < valLen; i++) { + values.push(buf.readDoubleLE(offset)); offset += 8; + } + + // tags + const tagLen = buf.readUInt32LE(offset); offset += 4; + const tags: string[] = []; + for (let i = 0; i < tagLen; i++) { + const len = buf.readUInt32LE(offset); offset += 4; + tags.push(buf.toString('utf8', offset, offset + len)); offset += len; + } + + return { id, timestamps, values, tags }; + } +); + + +// ============================================ +// 사용 예시 +// ============================================ + +// Order 쓰기 +const writer = new DataWriter('./data/orders', { + serializer: orderSerializer, +}); +writer.open(); + +writer.append({ + orderId: 1001, + items: [ + { sku: 'ITEM-A', qty: 2, price: 10.5 }, + { sku: 'ITEM-B', qty: 1, price: 25.0 }, + { sku: 'ITEM-C-LONG-SKU', qty: 5, price: 5.0 }, + ], + total: 71.0, +}); + +writer.append({ + orderId: 1002, + items: [{ sku: 'X', qty: 100, price: 1.0 }], + total: 100.0, +}); + +writer.close(); + +// Order 읽기 +const reader = new DataReader('./data/orders', orderSerializer); +reader.open(); + +const orders = reader.getBulkData(1, 2); +console.log(orders); +// [ +// { sequence: 1, data: { orderId: 1001, items: [...], total: 71 } }, +// { sequence: 2, data: { orderId: 1002, items: [...], total: 100 } }, +// ] + +reader.close(); +``` + +--- + +## 가변 배열 직렬화 패턴 + +``` +┌─────────────────────────────────────────────────┐ +│ [길이 4bytes] [요소1] [요소2] ... [요소N] │ +└─────────────────────────────────────────────────┘ +``` + +| 타입 | 직렬화 방식 | +|------|-------------| +| `number[]` | `[len:4] [val:8] [val:8] ...` | +| `string[]` | `[len:4] [strLen:4] [str] [strLen:4] [str] ...` | +| `Object[]` | `[len:4] [obj1 필드들] [obj2 필드들] ...` | +| `bigint[]` | `[len:4] [val:8] [val:8] ...` | + +--- + +## 헬퍼 함수 (재사용) + +```typescript +// dat/binary-helpers.ts + +export class BinaryWriter { + private chunks: Buffer[] = []; + + writeUInt32(value: number): this { + const buf = Buffer.alloc(4); + buf.writeUInt32LE(value, 0); + this.chunks.push(buf); + return this; + } + + writeDouble(value: number): this { + const buf = Buffer.alloc(8); + buf.writeDoubleLE(value, 0); + this.chunks.push(buf); + return this; + } + + writeBigUInt64(value: bigint): this { + const buf = Buffer.alloc(8); + buf.writeBigUInt64LE(value, 0); + this.chunks.push(buf); + return this; + } + + writeString(value: string): this { + const strBuf = Buffer.from(value, 'utf8'); + this.writeUInt32(strBuf.length); + this.chunks.push(strBuf); + return this; + } + + writeNumberArray(values: number[]): this { + this.writeUInt32(values.length); + for (const v of values) this.writeDouble(v); + return this; + } + + writeStringArray(values: string[]): this { + this.writeUInt32(values.length); + for (const v of values) this.writeString(v); + return this; + } + + toBuffer(): Buffer { + return Buffer.concat(this.chunks); + } +} + +export class BinaryReader { + private offset = 0; + + constructor(private buf: Buffer) {} + + readUInt32(): number { + const v = this.buf.readUInt32LE(this.offset); + this.offset += 4; + return v; + } + + readDouble(): number { + const v = this.buf.readDoubleLE(this.offset); + this.offset += 8; + return v; + } + + readBigUInt64(): bigint { + const v = this.buf.readBigUInt64LE(this.offset); + this.offset += 8; + return v; + } + + readString(): string { + const len = this.readUInt32(); + const v = this.buf.toString('utf8', this.offset, this.offset + len); + this.offset += len; + return v; + } + + readNumberArray(): number[] { + const len = this.readUInt32(); + const arr: number[] = []; + for (let i = 0; i < len; i++) arr.push(this.readDouble()); + return arr; + } + + readStringArray(): string[] { + const len = this.readUInt32(); + const arr: string[] = []; + for (let i = 0; i < len; i++) arr.push(this.readString()); + return arr; + } +} +``` + +--- + +## 헬퍼 사용 예시 + +```typescript +import { createSerializer } from './src/data-file/index.js'; +import { BinaryWriter, BinaryReader } from './src/data-file/binary-helpers.js'; + +interface Order { + orderId: number; + items: { sku: string; qty: number; price: number }[]; + total: number; +} + +const orderSerializer = createSerializer( + (data) => { + const w = new BinaryWriter(); + w.writeUInt32(data.orderId); + w.writeUInt32(data.items.length); + for (const item of data.items) { + w.writeString(item.sku); + w.writeUInt32(item.qty); + w.writeDouble(item.price); + } + w.writeDouble(data.total); + return w.toBuffer(); + }, + (buf) => { + const r = new BinaryReader(buf); + const orderId = r.readUInt32(); + const itemsLen = r.readUInt32(); + const items = []; + for (let i = 0; i < itemsLen; i++) { + items.push({ + sku: r.readString(), + qty: r.readUInt32(), + price: r.readDouble(), + }); + } + const total = r.readDouble(); + return { orderId, items, total }; + } +); +``` \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a260fad --- /dev/null +++ b/README.md @@ -0,0 +1,181 @@ +## 사용 예시 + +```typescript +// example.ts +import { DataWriter, DataReader, jsonSerializer, createSerializer } from './index.js'; + +// ============================================ +// 1. JSON 직렬화 (간단한 경우) +// ============================================ + +interface UserLog { + userId: string; + action: string; + metadata: Record; +} + +// 쓰기 +const logWriter = new DataWriter('./data/logs', { + serializer: jsonSerializer(), + maxEntries: 100_000, +}); +logWriter.open(); + +logWriter.append({ userId: 'u1', action: 'login', metadata: { ip: '1.2.3.4' } }); +logWriter.append({ userId: 'u2', action: 'purchase', metadata: { amount: 100 } }); +logWriter.append({ userId: 'u3', action: 'logout', metadata: {} }); + +console.log(logWriter.getStats()); +logWriter.close(); + +// 읽기 +const logReader = new DataReader('./data/logs', jsonSerializer()); +logReader.open(); + +// 단일 조회 +const single = logReader.getBySequence(2); +console.log('Single:', single); +// { sequence: 2, timestamp: 1234567890n, data: { userId: 'u2', action: 'purchase', ... } } + +// 범위 조회 +const bulk = logReader.getBulkData(1, 3); +console.log('Bulk:', bulk); +// [{ sequence: 1, ... }, { sequence: 2, ... }, { sequence: 3, ... }] + +// 전체 조회 +const all = logReader.getAllData(); +console.log('Total:', all.length); + +logReader.close(); + + +// ============================================ +// 2. 커스텀 바이너리 직렬화 (고성능) +// ============================================ + +interface SensorData { + sensorId: number; + temperature: number; + humidity: number; +} + +const sensorSerializer = createSerializer( + // serialize + (data) => { + const buf = Buffer.alloc(12); + buf.writeUInt32LE(data.sensorId, 0); + buf.writeFloatLE(data.temperature, 4); + buf.writeFloatLE(data.humidity, 8); + return buf; + }, + // deserialize + (buf) => ({ + sensorId: buf.readUInt32LE(0), + temperature: buf.readFloatLE(4), + humidity: buf.readFloatLE(8), + }) +); + +const sensorWriter = new DataWriter('./data/sensors', { + serializer: sensorSerializer, + maxEntries: 1_000_000, +}); +sensorWriter.open(); + +// 대량 추가 +for (let i = 0; i < 10000; i++) { + sensorWriter.append({ + sensorId: i % 100, + temperature: 20 + Math.random() * 10, + humidity: 40 + Math.random() * 30, + }); +} + +sensorWriter.close(); + +const sensorReader = new DataReader('./data/sensors', sensorSerializer); +sensorReader.open(); + +// 범위 조회 +const range = sensorReader.getBulkData(5000, 5010); +console.log('Range:', range.length, 'records'); + +// 타임스탬프 범위 조회 +const now = BigInt(Date.now()) * 1000000n; +const oneHourAgo = now - 3600n * 1000000000n; +const byTime = sensorReader.getBulkDataByTime(oneHourAgo, now); +console.log('By time:', byTime.length, 'records'); + +sensorReader.close(); + + +// ============================================ +// 3. 벌크 추가 +// ============================================ + +interface Order { + orderId: string; + amount: number; + status: string; +} + +const orderWriter = new DataWriter('./data/orders', { + serializer: jsonSerializer(), +}); +orderWriter.open(); + +const orders: Order[] = [ + { orderId: 'O-001', amount: 150, status: 'pending' }, + { orderId: 'O-002', amount: 250, status: 'completed' }, + { orderId: 'O-003', amount: 350, status: 'shipped' }, +]; + +const sequences = orderWriter.appendBulk(orders); +console.log('Added sequences:', sequences); // [1, 2, 3] + +orderWriter.close(); +``` + +--- + +## API 요약 + +### DataWriter + +| 메서드 | 설명 | +|--------|------| +| `open()` | 파일 열기 (없으면 생성) | +| `append(data, timestamp?)` | 레코드 추가, 시퀀스 반환 | +| `appendBulk(records, timestamp?)` | 여러 레코드 추가, 시퀀스 배열 반환 | +| `getLastSequence()` | 마지막 시퀀스 번호 | +| `getNextSequence()` | 다음 시퀀스 번호 | +| `sync()` | 디스크에 동기화 | +| `close()` | 파일 닫기 | +| `getStats()` | 상태 정보 | + +### DataReader + +| 메서드 | 설명 | +|--------|------| +| `open()` | 파일 열기 | +| `getBySequence(seq)` | 시퀀스로 단일 조회 | +| `getByIndex(index)` | 인덱스로 단일 조회 | +| `getBulkData(startSeq, endSeq)` | 시퀀스 범위 조회 | +| `getBulkDataByTime(startTs, endTs)` | 타임스탬프 범위 조회 | +| `getAllData()` | 전체 조회 | +| `getRecordCount()` | 레코드 수 | +| `getLastSequence()` | 마지막 시퀀스 | +| `close()` | 파일 닫기 | + +### Serializers + +```typescript +// JSON (범용) +jsonSerializer() + +// MessagePack (빠름, npm install @msgpack/msgpack 필요) +msgpackSerializer() + +// 커스텀 바이너리 +createSerializer(serialize, deserialize) +``` \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..689f971 --- /dev/null +++ b/index.ts @@ -0,0 +1 @@ +export * from './lib/dat/index.js'; diff --git a/lib/dat/binary-helpers.ts b/lib/dat/binary-helpers.ts new file mode 100644 index 0000000..a6d72f3 --- /dev/null +++ b/lib/dat/binary-helpers.ts @@ -0,0 +1,92 @@ +export class BinaryWriter { + private chunks: Buffer[] = []; + + writeUInt32(value: number): this { + const buf = Buffer.alloc(4); + buf.writeUInt32LE(value, 0); + this.chunks.push(buf); + return this; + } + + writeDouble(value: number): this { + const buf = Buffer.alloc(8); + buf.writeDoubleLE(value, 0); + this.chunks.push(buf); + return this; + } + + writeBigUInt64(value: bigint): this { + const buf = Buffer.alloc(8); + buf.writeBigUInt64LE(value, 0); + this.chunks.push(buf); + return this; + } + + writeString(value: string): this { + const strBuf = Buffer.from(value, 'utf8'); + this.writeUInt32(strBuf.length); + this.chunks.push(strBuf); + return this; + } + + writeNumberArray(values: number[]): this { + this.writeUInt32(values.length); + for (const v of values) this.writeDouble(v); + return this; + } + + writeStringArray(values: string[]): this { + this.writeUInt32(values.length); + for (const v of values) this.writeString(v); + return this; + } + + toBuffer(): Buffer { + return Buffer.concat(this.chunks); + } +} + +export class BinaryReader { + private offset = 0; + + constructor(private buf: Buffer) {} + + readUInt32(): number { + const v = this.buf.readUInt32LE(this.offset); + this.offset += 4; + return v; + } + + readDouble(): number { + const v = this.buf.readDoubleLE(this.offset); + this.offset += 8; + return v; + } + + readBigUInt64(): bigint { + const v = this.buf.readBigUInt64LE(this.offset); + this.offset += 8; + return v; + } + + readString(): string { + const len = this.readUInt32(); + const v = this.buf.toString('utf8', this.offset, this.offset + len); + this.offset += len; + return v; + } + + readNumberArray(): number[] { + const len = this.readUInt32(); + const arr: number[] = []; + for (let i = 0; i < len; i++) arr.push(this.readDouble()); + return arr; + } + + readStringArray(): string[] { + const len = this.readUInt32(); + const arr: string[] = []; + for (let i = 0; i < len; i++) arr.push(this.readString()); + return arr; + } +} \ No newline at end of file diff --git a/lib/dat/constants.ts b/lib/dat/constants.ts new file mode 100644 index 0000000..d69a5af --- /dev/null +++ b/lib/dat/constants.ts @@ -0,0 +1,5 @@ +// src/data-file/constants.ts +export const DATA_MAGIC = 'DATA'; +export const DATA_VERSION = 1; +export const DATA_HEADER_SIZE = 64; +export const RECORD_HEADER_SIZE = 8; \ No newline at end of file diff --git a/lib/dat/index.ts b/lib/dat/index.ts new file mode 100644 index 0000000..3d4fb5a --- /dev/null +++ b/lib/dat/index.ts @@ -0,0 +1,7 @@ +// src/data-file/index.ts +export { DataWriter } from './writer.js'; +export { DataReader } from './reader.js'; +export { DataProtocol } from './protocol.js'; +export * from './types.js'; +export * from './constants.js'; +export * from './serializers.js'; \ No newline at end of file diff --git a/lib/dat/protocol.ts b/lib/dat/protocol.ts new file mode 100644 index 0000000..411f829 --- /dev/null +++ b/lib/dat/protocol.ts @@ -0,0 +1,80 @@ +// src/data-file/protocol.ts +import { DATA_MAGIC, DATA_VERSION, DATA_HEADER_SIZE, RECORD_HEADER_SIZE } from './constants.js'; +import { crc32 } from '../idx/index.js'; +import type { Serializer } from './types.js'; + +export interface DataHeader { + magic: string; + version: number; + createdAt: bigint; + fileSize: bigint; + recordCount: number; + reserved: Buffer; +} + +export class DataProtocol { + static createHeader(): Buffer { + const buf = Buffer.alloc(DATA_HEADER_SIZE); + buf.write(DATA_MAGIC, 0, 4, 'ascii'); + buf.writeUInt32LE(DATA_VERSION, 4); + buf.writeBigUInt64LE(BigInt(Date.now()) * 1000000n, 8); + buf.writeBigUInt64LE(BigInt(DATA_HEADER_SIZE), 16); + buf.writeUInt32LE(0, 24); + return buf; + } + + static readHeader(buf: Buffer): DataHeader { + return { + magic: buf.toString('ascii', 0, 4), + version: buf.readUInt32LE(4), + createdAt: buf.readBigUInt64LE(8), + fileSize: buf.readBigUInt64LE(16), + recordCount: buf.readUInt32LE(24), + reserved: buf.subarray(28, 64), + }; + } + + static updateHeader(buf: Buffer, fileSize: bigint, recordCount: number): void { + buf.writeBigUInt64LE(fileSize, 16); + buf.writeUInt32LE(recordCount, 24); + } + + static serializeRecord(data: T, serializer: Serializer): Buffer { + const dataBytes = serializer.serialize(data); + const totalLen = RECORD_HEADER_SIZE + dataBytes.length; + + const buf = Buffer.alloc(totalLen); + + dataBytes.copy(buf, RECORD_HEADER_SIZE); + + buf.writeUInt32LE(dataBytes.length, 0); + const checksum = crc32(buf, RECORD_HEADER_SIZE, totalLen); + buf.writeUInt32LE(checksum, 4); + + return buf; + } + + static deserializeRecord( + buf: Buffer, + offset: number, + serializer: Serializer + ): { data: T; length: number } | null { + if (offset + RECORD_HEADER_SIZE > buf.length) return null; + + const dataLen = buf.readUInt32LE(offset); + const storedChecksum = buf.readUInt32LE(offset + 4); + + const totalLen = RECORD_HEADER_SIZE + dataLen; + if (offset + totalLen > buf.length) return null; + + const calcChecksum = crc32(buf, offset + RECORD_HEADER_SIZE, offset + totalLen); + if (calcChecksum !== storedChecksum) { + throw new Error(`Checksum mismatch at offset ${offset}`); + } + + const dataBytes = buf.subarray(offset + RECORD_HEADER_SIZE, offset + totalLen); + const data = serializer.deserialize(dataBytes); + + return { data, length: totalLen }; + } +} \ No newline at end of file diff --git a/lib/dat/reader.ts b/lib/dat/reader.ts new file mode 100644 index 0000000..a071846 --- /dev/null +++ b/lib/dat/reader.ts @@ -0,0 +1,213 @@ +// src/data-file/reader.ts +import * as fs from 'node:fs'; +import mmap from '@elilee/mmap-native'; +import { DATA_HEADER_SIZE } from './constants.js'; +import { DataProtocol, DataHeader } from './protocol.js'; +import { IndexReader } from '../idx/index.js'; +import type { Serializer, DataEntry } from './types.js'; + +export class DataReader { + private fd: number | null = null; + private buffer: Buffer | null = null; + private header: DataHeader | null = null; + + private indexReader: IndexReader; + private serializer: Serializer; + + readonly dataPath: string; + readonly indexPath: string; + + constructor(basePath: string, serializer: Serializer) { + this.dataPath = `${basePath}.dat`; + this.indexPath = `${basePath}.idx`; + this.serializer = serializer; + this.indexReader = new IndexReader(this.indexPath); + } + + open(): void { + const stats = fs.statSync(this.dataPath); + this.fd = fs.openSync(this.dataPath, 'r'); + + this.buffer = mmap.map( + stats.size, + mmap.PROT_READ, + mmap.MAP_SHARED, + this.fd, + 0 + ); + + this.header = DataProtocol.readHeader(this.buffer); + this.indexReader.open(); + } + + getHeader(): DataHeader { + if (!this.header) throw new Error('Data file not opened'); + return this.header; + } + + getBySequence(sequence: number): DataEntry | null { + if (!this.buffer) throw new Error('Data file not opened'); + + const found = this.indexReader.binarySearchBySequence(sequence); + if (!found) return null; + + const result = DataProtocol.deserializeRecord( + this.buffer, + Number(found.entry.offset), + this.serializer + ); + if (!result) return null; + + return { + sequence: found.entry.sequence, + timestamp: found.entry.timestamp, + data: result.data, + }; + } + + getByIndex(index: number): DataEntry | null { + if (!this.buffer) throw new Error('Data file not opened'); + + const entry = this.indexReader.getEntry(index); + if (!entry) return null; + + const result = DataProtocol.deserializeRecord( + this.buffer, + Number(entry.offset), + this.serializer + ); + if (!result) return null; + + return { + sequence: entry.sequence, + timestamp: entry.timestamp, + data: result.data, + }; + } + + getBulkData(startSeq: number, endSeq: number): DataEntry[] { + if (!this.buffer) throw new Error('Data file not opened'); + + const results: DataEntry[] = []; + const indexHeader = this.indexReader.getHeader(); + let startIdx = this.findStartIndex(startSeq, indexHeader.validCount); + + for (let i = startIdx; i < indexHeader.validCount; i++) { + const entry = this.indexReader.getEntry(i); + if (!entry) continue; + + if (entry.sequence > endSeq) break; + + if (entry.sequence >= startSeq) { + const result = DataProtocol.deserializeRecord( + this.buffer, + Number(entry.offset), + this.serializer + ); + if (result) { + results.push({ + sequence: entry.sequence, + timestamp: entry.timestamp, + data: result.data, + }); + } + } + } + + return results; + } + + private findStartIndex(targetSeq: number, validCount: number): number { + let left = 0; + let right = validCount - 1; + let result = 0; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const entry = this.indexReader.getEntry(mid); + + if (!entry) { + right = mid - 1; + continue; + } + + if (entry.sequence >= targetSeq) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return result; + } + + getBulkDataByTime(startTs: bigint, endTs: bigint): DataEntry[] { + if (!this.buffer) throw new Error('Data file not opened'); + + const indexResults = this.indexReader.findByTimeRange(startTs, endTs); + const results: DataEntry[] = []; + + for (const { entry } of indexResults) { + const result = DataProtocol.deserializeRecord( + this.buffer, + Number(entry.offset), + this.serializer + ); + if (result) { + results.push({ + sequence: entry.sequence, + timestamp: entry.timestamp, + data: result.data, + }); + } + } + + return results; + } + + getAllData(): DataEntry[] { + if (!this.buffer) throw new Error('Data file not opened'); + + const entries = this.indexReader.getAllEntries(); + const results: DataEntry[] = []; + + for (const entry of entries) { + const result = DataProtocol.deserializeRecord( + this.buffer, + Number(entry.offset), + this.serializer + ); + if (result) { + results.push({ + sequence: entry.sequence, + timestamp: entry.timestamp, + data: result.data, + }); + } + } + + return results; + } + + getRecordCount(): number { + return this.indexReader.getHeader().validCount; + } + + getLastSequence(): number { + return this.indexReader.getHeader().lastSequence; + } + + close(): void { + if (this.buffer) { + mmap.unmap(this.buffer); + this.buffer = null; + } + if (this.fd !== null) { + fs.closeSync(this.fd); + this.fd = null; + } + this.header = null; + this.indexReader.close(); + } +} \ No newline at end of file diff --git a/lib/dat/serializers.ts b/lib/dat/serializers.ts new file mode 100644 index 0000000..d0aa2b9 --- /dev/null +++ b/lib/dat/serializers.ts @@ -0,0 +1,32 @@ +// src/data-file/serializers.ts +import type { Serializer } from './types.js'; + +export function jsonSerializer(): Serializer { + return { + serialize(data: T): Buffer { + return Buffer.from(JSON.stringify(data), 'utf8'); + }, + deserialize(buf: Buffer): T { + return JSON.parse(buf.toString('utf8')); + }, + }; +} + +export function msgpackSerializer(): Serializer { + const { encode, decode } = require('@msgpack/msgpack'); + return { + serialize(data: T): Buffer { + return Buffer.from(encode(data)); + }, + deserialize(buf: Buffer): T { + return decode(buf) as T; + }, + }; +} + +export function createSerializer( + serialize: (data: T) => Buffer, + deserialize: (buf: Buffer) => T +): Serializer { + return { serialize, deserialize }; +} \ No newline at end of file diff --git a/lib/dat/types.ts b/lib/dat/types.ts new file mode 100644 index 0000000..b237bdb --- /dev/null +++ b/lib/dat/types.ts @@ -0,0 +1,16 @@ +// src/data-file/types.ts +export interface Serializer { + serialize(data: T): Buffer; + deserialize(buf: Buffer): T; +} + +export interface DataEntry { + sequence: number; + timestamp: bigint; + data: T; +} + +export interface DataFileOptions { + serializer: Serializer; + maxEntries?: number; +} \ No newline at end of file diff --git a/lib/dat/writer.ts b/lib/dat/writer.ts new file mode 100644 index 0000000..3a75a5f --- /dev/null +++ b/lib/dat/writer.ts @@ -0,0 +1,120 @@ +// src/data-file/writer.ts +import * as fs from 'node:fs'; +import { DATA_HEADER_SIZE } from './constants.js'; +import { DataProtocol } from './protocol.js'; +import { IndexWriter } from '../idx/index.js'; +import type { Serializer, DataFileOptions } from './types.js'; + +export class DataWriter { + private fd: number | null = null; + private headerBuf: Buffer | null = null; + private currentOffset: bigint = BigInt(DATA_HEADER_SIZE); + private recordCount = 0; + + private indexWriter: IndexWriter; + private serializer: Serializer; + + readonly dataPath: string; + readonly indexPath: string; + + constructor(basePath: string, options: DataFileOptions) { + this.dataPath = `${basePath}.dat`; + this.indexPath = `${basePath}.idx`; + this.serializer = options.serializer; + + const maxEntries = options.maxEntries ?? 10_000_000; + this.indexWriter = new IndexWriter(this.indexPath, { maxEntries }); + } + + open(): void { + const isNew = !fs.existsSync(this.dataPath); + + this.fd = fs.openSync(this.dataPath, isNew ? 'w+' : 'r+'); + this.headerBuf = Buffer.alloc(DATA_HEADER_SIZE); + + if (isNew) { + const header = DataProtocol.createHeader(); + fs.writeSync(this.fd, header, 0, DATA_HEADER_SIZE, 0); + this.currentOffset = BigInt(DATA_HEADER_SIZE); + this.recordCount = 0; + } else { + fs.readSync(this.fd, this.headerBuf, 0, DATA_HEADER_SIZE, 0); + const header = DataProtocol.readHeader(this.headerBuf); + this.currentOffset = header.fileSize; + this.recordCount = header.recordCount; + } + + this.indexWriter.open(); + } + + append(data: T, timestamp?: bigint): number { + if (this.fd === null) throw new Error('Data file not opened'); + + const buf = DataProtocol.serializeRecord(data, this.serializer); + const offset = this.currentOffset; + + fs.writeSync(this.fd, buf, 0, buf.length, Number(offset)); + + const sequence = this.indexWriter.getNextSequence(); + const ts = timestamp ?? BigInt(Date.now()) * 1000000n; + + this.indexWriter.append(offset, buf.length, ts); + + this.currentOffset += BigInt(buf.length); + this.recordCount++; + + return sequence; + } + + appendBulk(records: T[], timestamp?: bigint): number[] { + const sequences: number[] = []; + const ts = timestamp ?? BigInt(Date.now()) * 1000000n; + + for (const record of records) { + const seq = this.append(record, ts); + sequences.push(seq); + } + + return sequences; + } + + getLastSequence(): number { + return this.indexWriter.getLastSequence(); + } + + getNextSequence(): number { + return this.indexWriter.getNextSequence(); + } + + sync(): void { + if (this.fd === null || !this.headerBuf) return; + + DataProtocol.updateHeader(this.headerBuf, this.currentOffset, this.recordCount); + fs.writeSync(this.fd, this.headerBuf, 0, DATA_HEADER_SIZE, 0); + fs.fsyncSync(this.fd); + + this.indexWriter.syncAll(); + } + + close(): void { + this.sync(); + + if (this.fd !== null) { + fs.closeSync(this.fd); + this.fd = null; + } + + this.indexWriter.close(); + this.headerBuf = null; + } + + getStats() { + return { + dataPath: this.dataPath, + indexPath: this.indexPath, + currentOffset: this.currentOffset, + recordCount: this.recordCount, + lastSequence: this.indexWriter.getLastSequence(), + }; + } +} \ No newline at end of file diff --git a/lib/idx/constants.ts b/lib/idx/constants.ts new file mode 100644 index 0000000..e0c3d1c --- /dev/null +++ b/lib/idx/constants.ts @@ -0,0 +1,8 @@ +// src/index-file/constants.ts +export const INDEX_MAGIC = 'INDX'; +export const INDEX_VERSION = 1; +export const INDEX_HEADER_SIZE = 64; +export const INDEX_ENTRY_SIZE = 32; + +export const FLAG_VALID = 0x0001; +export const FLAG_DELETED = 0x0002; \ No newline at end of file diff --git a/lib/idx/index.ts b/lib/idx/index.ts new file mode 100644 index 0000000..3a6f4aa --- /dev/null +++ b/lib/idx/index.ts @@ -0,0 +1,6 @@ +// src/index-file/index.ts +export { IndexWriter } from './writer.js'; +export { IndexReader } from './reader.js'; +export { IndexProtocol, crc32 } from './protocol.js'; +export * from './types.js'; +export * from './constants.js'; \ No newline at end of file diff --git a/lib/idx/protocol.ts b/lib/idx/protocol.ts new file mode 100644 index 0000000..2423006 --- /dev/null +++ b/lib/idx/protocol.ts @@ -0,0 +1,106 @@ +// src/index-file/protocol.ts +import { + INDEX_MAGIC, + INDEX_VERSION, + INDEX_HEADER_SIZE, + INDEX_ENTRY_SIZE, + FLAG_VALID, +} from './constants.js'; +import type { IndexHeader, IndexEntry } from './types.js'; + +const CRC_TABLE = new Uint32Array(256); +for (let i = 0; i < 256; i++) { + let c = i; + for (let j = 0; j < 8; j++) { + c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); + } + CRC_TABLE[i] = c >>> 0; +} + +export function crc32(buf: Buffer, start = 0, end?: number): number { + let crc = 0xFFFFFFFF; + const len = end ?? buf.length; + for (let i = start; i < len; i++) { + crc = CRC_TABLE[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8); + } + return (~crc) >>> 0; +} + +export class IndexProtocol { + static createHeader(entryCount: number, magic = INDEX_MAGIC): Buffer { + const buf = Buffer.alloc(INDEX_HEADER_SIZE); + buf.write(magic, 0, 4, 'ascii'); + buf.writeUInt32LE(INDEX_VERSION, 4); + buf.writeBigUInt64LE(BigInt(Date.now()) * 1000000n, 8); + buf.writeUInt32LE(INDEX_ENTRY_SIZE, 16); + buf.writeUInt32LE(entryCount, 20); + buf.writeUInt32LE(0, 24); + buf.writeBigUInt64LE(0n, 28); + buf.writeUInt32LE(0, 36); + return buf; + } + + static readHeader(buf: Buffer): IndexHeader { + return { + magic: buf.toString('ascii', 0, 4), + version: buf.readUInt32LE(4), + createdAt: buf.readBigUInt64LE(8), + entrySize: buf.readUInt32LE(16), + entryCount: buf.readUInt32LE(20), + validCount: buf.readUInt32LE(24), + dataFileSize: buf.readBigUInt64LE(28), + lastSequence: buf.readUInt32LE(36), + reserved: buf.subarray(40, 64), + }; + } + + static updateHeaderCounts( + buf: Buffer, + validCount: number, + dataFileSize: bigint, + lastSequence: number + ): void { + buf.writeUInt32LE(validCount, 24); + buf.writeBigUInt64LE(dataFileSize, 28); + buf.writeUInt32LE(lastSequence, 36); + } + + static writeEntry(buf: Buffer, index: number, entry: Omit): void { + const off = INDEX_HEADER_SIZE + index * INDEX_ENTRY_SIZE; + + buf.writeUInt32LE(entry.sequence, off); + buf.writeBigUInt64LE(entry.timestamp, off + 4); + buf.writeBigUInt64LE(entry.offset, off + 12); + buf.writeUInt32LE(entry.length, off + 20); + buf.writeUInt32LE(entry.flags | FLAG_VALID, off + 24); + + const checksum = crc32(buf, off, off + 28); + buf.writeUInt32LE(checksum, off + 28); + } + + static readEntry(buf: Buffer, index: number): IndexEntry | null { + const off = INDEX_HEADER_SIZE + index * INDEX_ENTRY_SIZE; + const flags = buf.readUInt32LE(off + 24); + + if (!(flags & FLAG_VALID)) return null; + + return { + sequence: buf.readUInt32LE(off), + timestamp: buf.readBigUInt64LE(off + 4), + offset: buf.readBigUInt64LE(off + 12), + length: buf.readUInt32LE(off + 20), + flags, + checksum: buf.readUInt32LE(off + 28), + }; + } + + static isValidEntry(buf: Buffer, index: number): boolean { + const off = INDEX_HEADER_SIZE + index * INDEX_ENTRY_SIZE; + const flags = buf.readUInt32LE(off + 24); + return (flags & FLAG_VALID) !== 0; + } + + static calcFileSize(entryCount: number): number { + return INDEX_HEADER_SIZE + INDEX_ENTRY_SIZE * entryCount; + } +} \ No newline at end of file diff --git a/lib/idx/reader.ts b/lib/idx/reader.ts new file mode 100644 index 0000000..256a12b --- /dev/null +++ b/lib/idx/reader.ts @@ -0,0 +1,131 @@ +// src/index-file/reader.ts +import * as fs from 'node:fs'; +import mmap from '@elilee/mmap-native'; +import { IndexProtocol } from './protocol.js'; +import type { IndexHeader, IndexEntry } from './types.js'; + +export class IndexReader { + private fd: number | null = null; + private buffer: Buffer | null = null; + private header: IndexHeader | null = null; + + readonly path: string; + + constructor(path: string) { + this.path = path; + } + + open(): void { + const stats = fs.statSync(this.path); + this.fd = fs.openSync(this.path, 'r'); + + this.buffer = mmap.map( + stats.size, + mmap.PROT_READ, + mmap.MAP_SHARED, + this.fd, + 0 + ); + + this.header = IndexProtocol.readHeader(this.buffer); + } + + getHeader(): IndexHeader { + if (!this.header) throw new Error('Index file not opened'); + return this.header; + } + + getEntry(index: number): IndexEntry | null { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + if (index < 0 || index >= this.header.entryCount) return null; + return IndexProtocol.readEntry(this.buffer, index); + } + + findBySequence(sequence: number): { index: number; entry: IndexEntry } | null { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + + for (let i = 0; i < this.header.validCount; i++) { + const entry = IndexProtocol.readEntry(this.buffer, i); + if (entry && entry.sequence === sequence) { + return { index: i, entry }; + } + } + return null; + } + + findBySequenceRange(startSeq: number, endSeq: number): { index: number; entry: IndexEntry }[] { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + + const results: { index: number; entry: IndexEntry }[] = []; + for (let i = 0; i < this.header.validCount; i++) { + const entry = IndexProtocol.readEntry(this.buffer, i); + if (entry && entry.sequence >= startSeq && entry.sequence <= endSeq) { + results.push({ index: i, entry }); + } + } + return results; + } + + getAllEntries(): IndexEntry[] { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + + const entries: IndexEntry[] = []; + for (let i = 0; i < this.header.validCount; i++) { + const entry = IndexProtocol.readEntry(this.buffer, i); + if (entry) entries.push(entry); + } + return entries; + } + + findByTimeRange(startTs: bigint, endTs: bigint): { index: number; entry: IndexEntry }[] { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + + const results: { index: number; entry: IndexEntry }[] = []; + for (let i = 0; i < this.header.validCount; i++) { + const entry = IndexProtocol.readEntry(this.buffer, i); + if (entry && entry.timestamp >= startTs && entry.timestamp <= endTs) { + results.push({ index: i, entry }); + } + } + return results; + } + + binarySearchBySequence(targetSeq: number): { index: number; entry: IndexEntry } | null { + if (!this.buffer || !this.header) throw new Error('Index file not opened'); + + let left = 0; + let right = this.header.validCount - 1; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const entry = IndexProtocol.readEntry(this.buffer, mid); + + if (!entry) { + right = mid - 1; + continue; + } + + if (entry.sequence === targetSeq) { + return { index: mid, entry }; + } else if (entry.sequence < targetSeq) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return null; + } + + close(): void { + if (this.buffer) { + mmap.unmap(this.buffer); + this.buffer = null; + } + if (this.fd !== null) { + fs.closeSync(this.fd); + this.fd = null; + } + this.header = null; + } +} \ No newline at end of file diff --git a/lib/idx/types.ts b/lib/idx/types.ts new file mode 100644 index 0000000..a677b3b --- /dev/null +++ b/lib/idx/types.ts @@ -0,0 +1,26 @@ +// src/index-file/types.ts +export interface IndexHeader { + magic: string; + version: number; + createdAt: bigint; + entrySize: number; + entryCount: number; + validCount: number; + dataFileSize: bigint; + lastSequence: number; + reserved: Buffer; +} + +export interface IndexEntry { + sequence: number; + timestamp: bigint; + offset: bigint; + length: number; + flags: number; + checksum: number; +} + +export interface IndexFileOptions { + maxEntries: number; + magic?: string; +} \ No newline at end of file diff --git a/lib/idx/writer.ts b/lib/idx/writer.ts new file mode 100644 index 0000000..632d607 --- /dev/null +++ b/lib/idx/writer.ts @@ -0,0 +1,141 @@ +// src/index-file/writer.ts +import * as fs from 'node:fs'; +import mmap from '@elilee/mmap-native'; +import { INDEX_HEADER_SIZE, FLAG_VALID } from './constants.js'; +import { IndexProtocol } from './protocol.js'; +import type { IndexFileOptions } from './types.js'; + +export class IndexWriter { + private fd: number | null = null; + private buffer: Buffer | null = null; + private validCount = 0; + private dataFileSize = 0n; + private lastSequence = 0; + + readonly path: string; + readonly maxEntries: number; + readonly fileSize: number; + + constructor(path: string, options: IndexFileOptions) { + this.path = path; + this.maxEntries = options.maxEntries; + this.fileSize = IndexProtocol.calcFileSize(options.maxEntries); + } + + open(): void { + const isNew = !fs.existsSync(this.path); + + this.fd = fs.openSync(this.path, isNew ? 'w+' : 'r+'); + + if (isNew) { + fs.ftruncateSync(this.fd, this.fileSize); + } + + this.buffer = mmap.map( + this.fileSize, + mmap.PROT_READ | mmap.PROT_WRITE, + mmap.MAP_SHARED, + this.fd, + 0 + ); + + if (isNew) { + const header = IndexProtocol.createHeader(this.maxEntries); + header.copy(this.buffer, 0); + this.syncHeader(); + } else { + const header = IndexProtocol.readHeader(this.buffer); + this.validCount = header.validCount; + this.dataFileSize = header.dataFileSize; + this.lastSequence = header.lastSequence; + } + } + + write( + index: number, + sequence: number, + offset: bigint, + length: number, + timestamp?: bigint + ): boolean { + if (!this.buffer) throw new Error('Index file not opened'); + if (index < 0 || index >= this.maxEntries) return false; + + const ts = timestamp ?? BigInt(Date.now()) * 1000000n; + + IndexProtocol.writeEntry(this.buffer, index, { + sequence, + timestamp: ts, + offset, + length, + flags: FLAG_VALID, + }); + + this.validCount++; + if (sequence > this.lastSequence) { + this.lastSequence = sequence; + } + + const newDataEnd = offset + BigInt(length); + if (newDataEnd > this.dataFileSize) { + this.dataFileSize = newDataEnd; + } + + return true; + } + + append(offset: bigint, length: number, timestamp?: bigint): number { + const index = this.validCount; + if (index >= this.maxEntries) return -1; + + const sequence = this.lastSequence + 1; + this.write(index, sequence, offset, length, timestamp); + return index; + } + + getLastSequence(): number { + return this.lastSequence; + } + + getNextSequence(): number { + return this.lastSequence + 1; + } + + syncHeader(): void { + if (!this.buffer) return; + IndexProtocol.updateHeaderCounts( + this.buffer, + this.validCount, + this.dataFileSize, + this.lastSequence + ); + mmap.sync(this.buffer, 0, INDEX_HEADER_SIZE, mmap.MS_ASYNC); + } + + syncAll(): void { + if (!this.buffer) return; + this.syncHeader(); + mmap.sync(this.buffer, 0, this.fileSize, mmap.MS_SYNC); + } + + close(): void { + if (!this.buffer || this.fd === null) return; + + this.syncAll(); + mmap.unmap(this.buffer); + fs.closeSync(this.fd); + + this.buffer = null; + this.fd = null; + } + + getStats() { + return { + path: this.path, + maxEntries: this.maxEntries, + validCount: this.validCount, + dataFileSize: this.dataFileSize, + lastSequence: this.lastSequence, + }; + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..19d5fee --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1194 @@ +{ + "name": "@elilee/index-file", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@elilee/index-file", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@elilee/mmap-native": "git+https://git.satitech.co.kr/sati-open/sati.n-api.mmap.git" + } + }, + "node_modules/@elilee/mmap-native": { + "version": "1.0.1", + "resolved": "git+https://git.satitech.co.kr/sati-open/sati.n-api.mmap.git#f6e0201088d713f4dc06e984fa1aa316a39f6e83", + "cpu": [ + "x64", + "arm64" + ], + "hasInstallScript": true, + "license": "MIT", + "os": [ + "linux", + "darwin" + ], + "dependencies": { + "@types/node": "^22.0.0", + "node-gyp": "^11.0.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@npmcli/agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", + "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/node": { + "version": "22.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache": { + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", + "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT" + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", + "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", + "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-gyp": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", + "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/unique-slug": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..20cc2e0 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "@elilee/index-file", + "version": "1.0.0", + "description": "Use index file with elilee mmap-native", + "license": "MIT", + "author": "", + "type": "module", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", + "files": [ + "dist/", + "lib/", + "tsconfig.json" + ], + "dependencies": { + "typescript": "^5.7.0", + "@types/node": "^22.0.0", + "@elilee/mmap-native": "git+https://git.satitech.co.kr/sati-open/sati.n-api.mmap.git" + }, + "scripts": { + "prepare": "tsc -p tsconfig.json", + "build:ts": "tsc -p tsconfig.json" + }, + "engines": { + "node": ">=18.0.0" + }, + "os": ["linux", "darwin"], + "cpu": ["x64", "arm64"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bd59385 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +// tsconfig.json +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist", + "rootDir": ".", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true + }, + "include": ["lib/**/*", "index.ts"], + "exclude": ["node_modules", "dist"] +}