modern-tar 0.7.2 → 0.7.4
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/fs/index.js +25 -27
- package/dist/{unpacker-EHw0ivci.js → unpacker-CEuY-276.js} +30 -19
- package/dist/web/index.js +46 -98
- package/package.json +9 -7
package/dist/fs/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as normalizeBody, c as LINK, l as SYMLINK, n as createTarPacker, o as DIRECTORY, r as transformHeader, s as FILE, t as createUnpacker } from "../unpacker-
|
|
1
|
+
import { a as normalizeBody, c as LINK, l as SYMLINK, n as createTarPacker, o as DIRECTORY, r as transformHeader, s as FILE, t as createUnpacker } from "../unpacker-CEuY-276.js";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import { cpus } from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
@@ -575,11 +575,13 @@ const createPathCache = (destDirPath, options) => {
|
|
|
575
575
|
}
|
|
576
576
|
const parentDir = path.dirname(outPath);
|
|
577
577
|
switch (type) {
|
|
578
|
-
case DIRECTORY:
|
|
578
|
+
case DIRECTORY: {
|
|
579
579
|
pathConflicts.set(normalizedName, DIRECTORY);
|
|
580
|
-
|
|
580
|
+
const safeMode = mode ? mode & 511 : void 0;
|
|
581
|
+
await prepareDirectory(outPath, dmode ?? safeMode);
|
|
581
582
|
if (mtime) await fs.lutimes(outPath, mtime, mtime).catch(() => {});
|
|
582
583
|
return;
|
|
584
|
+
}
|
|
583
585
|
case FILE:
|
|
584
586
|
pathConflicts.set(normalizedName, FILE);
|
|
585
587
|
await prepareDirectory(parentDir);
|
|
@@ -633,37 +635,34 @@ function unpackTar(directoryPath, options = {}) {
|
|
|
633
635
|
const pathCache = createPathCache(directoryPath, options);
|
|
634
636
|
let currentFileStream = null;
|
|
635
637
|
let currentWriteCallback = null;
|
|
636
|
-
let needsDiscardBody = false;
|
|
637
638
|
return new Writable({
|
|
638
639
|
async write(chunk, _, cb) {
|
|
639
640
|
try {
|
|
640
641
|
unpacker.write(chunk);
|
|
641
|
-
if (
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
else {
|
|
642
|
+
if (unpacker.isEntryActive()) {
|
|
643
|
+
if (currentFileStream && currentWriteCallback) {
|
|
644
|
+
let needsDrain = false;
|
|
645
|
+
const writeCallback = currentWriteCallback;
|
|
646
|
+
while (!unpacker.isBodyComplete()) {
|
|
647
|
+
needsDrain = false;
|
|
648
|
+
if (unpacker.streamBody(writeCallback) === 0) if (needsDrain) await currentFileStream.waitDrain();
|
|
649
|
+
else {
|
|
650
|
+
cb();
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
while (!unpacker.skipPadding()) {
|
|
655
655
|
cb();
|
|
656
656
|
return;
|
|
657
657
|
}
|
|
658
|
-
|
|
659
|
-
|
|
658
|
+
const streamToClose = currentFileStream;
|
|
659
|
+
if (streamToClose) opQueue.add(() => streamToClose.end());
|
|
660
|
+
currentFileStream = null;
|
|
661
|
+
currentWriteCallback = null;
|
|
662
|
+
} else if (!unpacker.skipEntry()) {
|
|
660
663
|
cb();
|
|
661
664
|
return;
|
|
662
665
|
}
|
|
663
|
-
const streamToClose = currentFileStream;
|
|
664
|
-
if (streamToClose) opQueue.add(() => streamToClose.end());
|
|
665
|
-
currentFileStream = null;
|
|
666
|
-
currentWriteCallback = null;
|
|
667
666
|
}
|
|
668
667
|
while (true) {
|
|
669
668
|
const header = unpacker.readHeader();
|
|
@@ -674,7 +673,6 @@ function unpackTar(directoryPath, options = {}) {
|
|
|
674
673
|
const transformedHeader = transformHeader(header, options);
|
|
675
674
|
if (!transformedHeader) {
|
|
676
675
|
if (!unpacker.skipEntry()) {
|
|
677
|
-
needsDiscardBody = true;
|
|
678
676
|
cb();
|
|
679
677
|
return;
|
|
680
678
|
}
|
|
@@ -682,8 +680,9 @@ function unpackTar(directoryPath, options = {}) {
|
|
|
682
680
|
}
|
|
683
681
|
const outPath = await opQueue.add(() => pathCache.preparePath(transformedHeader));
|
|
684
682
|
if (outPath) {
|
|
683
|
+
const safeMode = transformedHeader.mode ? transformedHeader.mode & 511 : void 0;
|
|
685
684
|
const fileStream = createFileSink(outPath, {
|
|
686
|
-
mode: options.fmode ??
|
|
685
|
+
mode: options.fmode ?? safeMode,
|
|
687
686
|
mtime: transformedHeader.mtime ?? void 0
|
|
688
687
|
});
|
|
689
688
|
let needsDrain = false;
|
|
@@ -710,7 +709,6 @@ function unpackTar(directoryPath, options = {}) {
|
|
|
710
709
|
}
|
|
711
710
|
opQueue.add(() => fileStream.end());
|
|
712
711
|
} else if (!unpacker.skipEntry()) {
|
|
713
|
-
needsDiscardBody = true;
|
|
714
712
|
cb();
|
|
715
713
|
return;
|
|
716
714
|
}
|
|
@@ -108,7 +108,7 @@ function readNumeric(view, offset, size) {
|
|
|
108
108
|
|
|
109
109
|
//#endregion
|
|
110
110
|
//#region src/tar/body.ts
|
|
111
|
-
const isBodyless = (header) => header.type === DIRECTORY || header.type === SYMLINK || header.type === LINK;
|
|
111
|
+
const isBodyless = (header) => header.type === DIRECTORY || header.type === SYMLINK || header.type === LINK || header.type === "character-device" || header.type === "block-device" || header.type === "fifo";
|
|
112
112
|
async function normalizeBody(body) {
|
|
113
113
|
if (body === null || body === void 0) return EMPTY;
|
|
114
114
|
if (body instanceof Uint8Array) return body;
|
|
@@ -256,6 +256,7 @@ function parseUstarHeader(block, strict) {
|
|
|
256
256
|
linkname: readString(block, USTAR_LINKNAME_OFFSET, USTAR_LINKNAME_SIZE)
|
|
257
257
|
};
|
|
258
258
|
const magic = readString(block, USTAR_MAGIC_OFFSET, USTAR_MAGIC_SIZE);
|
|
259
|
+
if (isBodyless(header)) header.size = 0;
|
|
259
260
|
if (magic.trim() === "ustar") {
|
|
260
261
|
header.uname = readString(block, USTAR_UNAME_OFFSET, USTAR_UNAME_SIZE);
|
|
261
262
|
header.gname = readString(block, USTAR_GNAME_OFFSET, USTAR_GNAME_SIZE);
|
|
@@ -275,8 +276,8 @@ const PAX_MAPPING = {
|
|
|
275
276
|
};
|
|
276
277
|
function parsePax(buffer) {
|
|
277
278
|
const decoder$1 = new TextDecoder("utf-8");
|
|
278
|
-
const overrides =
|
|
279
|
-
const pax =
|
|
279
|
+
const overrides = Object.create(null);
|
|
280
|
+
const pax = Object.create(null);
|
|
280
281
|
let offset = 0;
|
|
281
282
|
while (offset < buffer.length) {
|
|
282
283
|
const spaceIndex = buffer.indexOf(32, offset);
|
|
@@ -287,9 +288,8 @@ function parsePax(buffer) {
|
|
|
287
288
|
const [key, value] = decoder$1.decode(buffer.subarray(spaceIndex + 1, recordEnd - 1)).split("=", 2);
|
|
288
289
|
if (key && value !== void 0) {
|
|
289
290
|
pax[key] = value;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const [targetKey, parser] = mapping;
|
|
291
|
+
if (Object.hasOwn(PAX_MAPPING, key)) {
|
|
292
|
+
const [targetKey, parser] = PAX_MAPPING[key];
|
|
293
293
|
const parsedValue = parser(value);
|
|
294
294
|
if (typeof parsedValue === "string" || !Number.isNaN(parsedValue)) overrides[targetKey] = parsedValue;
|
|
295
295
|
}
|
|
@@ -562,16 +562,20 @@ function createChunkQueue() {
|
|
|
562
562
|
//#region src/tar/unpacker.ts
|
|
563
563
|
const STATE_HEADER = 0;
|
|
564
564
|
const STATE_BODY = 1;
|
|
565
|
+
const truncateErr = /* @__PURE__ */ new Error("Tar archive is truncated.");
|
|
565
566
|
function createUnpacker(options = {}) {
|
|
566
567
|
const strict = options.strict ?? false;
|
|
567
568
|
const { available, peek, push, discard, pull } = createChunkQueue();
|
|
568
569
|
let state = STATE_HEADER;
|
|
569
570
|
let ended = false;
|
|
571
|
+
let done = false;
|
|
570
572
|
let eof = false;
|
|
571
573
|
let currentEntry = null;
|
|
572
574
|
const paxGlobals = {};
|
|
573
575
|
let nextEntryOverrides = {};
|
|
574
576
|
const unpacker = {
|
|
577
|
+
isEntryActive: () => state === STATE_BODY,
|
|
578
|
+
isBodyComplete: () => !currentEntry || currentEntry.remaining === 0,
|
|
575
579
|
write(chunk) {
|
|
576
580
|
if (ended) throw new Error("Archive already ended.");
|
|
577
581
|
push(chunk);
|
|
@@ -581,12 +585,12 @@ function createUnpacker(options = {}) {
|
|
|
581
585
|
},
|
|
582
586
|
readHeader() {
|
|
583
587
|
if (state !== STATE_HEADER) throw new Error("Cannot read header while an entry is active");
|
|
584
|
-
if (
|
|
585
|
-
while (!
|
|
588
|
+
if (done) return void 0;
|
|
589
|
+
while (!done) {
|
|
586
590
|
if (available() < BLOCK_SIZE) {
|
|
587
591
|
if (ended) {
|
|
588
|
-
if (available() > 0 && strict) throw
|
|
589
|
-
|
|
592
|
+
if (available() > 0 && strict) throw truncateErr;
|
|
593
|
+
done = true;
|
|
590
594
|
return;
|
|
591
595
|
}
|
|
592
596
|
return null;
|
|
@@ -595,14 +599,15 @@ function createUnpacker(options = {}) {
|
|
|
595
599
|
if (isZeroBlock(headerBlock)) {
|
|
596
600
|
if (available() < BLOCK_SIZE * 2) {
|
|
597
601
|
if (ended) {
|
|
598
|
-
if (strict) throw
|
|
599
|
-
|
|
602
|
+
if (strict) throw truncateErr;
|
|
603
|
+
done = true;
|
|
600
604
|
return;
|
|
601
605
|
}
|
|
602
606
|
return null;
|
|
603
607
|
}
|
|
604
608
|
if (isZeroBlock(peek(BLOCK_SIZE * 2).subarray(BLOCK_SIZE))) {
|
|
605
609
|
discard(BLOCK_SIZE * 2);
|
|
610
|
+
done = true;
|
|
606
611
|
eof = true;
|
|
607
612
|
return;
|
|
608
613
|
}
|
|
@@ -622,7 +627,7 @@ function createUnpacker(options = {}) {
|
|
|
622
627
|
if (metaParser) {
|
|
623
628
|
const paddedSize = internalHeader.size + BLOCK_SIZE_MASK & ~BLOCK_SIZE_MASK;
|
|
624
629
|
if (available() < BLOCK_SIZE + paddedSize) {
|
|
625
|
-
if (ended && strict) throw
|
|
630
|
+
if (ended && strict) throw truncateErr;
|
|
626
631
|
return null;
|
|
627
632
|
}
|
|
628
633
|
discard(BLOCK_SIZE);
|
|
@@ -636,6 +641,7 @@ function createUnpacker(options = {}) {
|
|
|
636
641
|
if (internalHeader.prefix) header.name = `${internalHeader.prefix}/${header.name}`;
|
|
637
642
|
applyOverrides(header, paxGlobals);
|
|
638
643
|
applyOverrides(header, nextEntryOverrides);
|
|
644
|
+
if (header.name.endsWith("/") && header.type === FILE) header.type = DIRECTORY;
|
|
639
645
|
nextEntryOverrides = {};
|
|
640
646
|
currentEntry = {
|
|
641
647
|
header,
|
|
@@ -654,9 +660,6 @@ function createUnpacker(options = {}) {
|
|
|
654
660
|
currentEntry.remaining -= fed;
|
|
655
661
|
return fed;
|
|
656
662
|
},
|
|
657
|
-
isBodyComplete() {
|
|
658
|
-
return !currentEntry || currentEntry.remaining === 0;
|
|
659
|
-
},
|
|
660
663
|
skipPadding() {
|
|
661
664
|
if (state !== STATE_BODY || !currentEntry) return true;
|
|
662
665
|
if (currentEntry.remaining > 0) throw new Error("Body not fully consumed");
|
|
@@ -668,12 +671,20 @@ function createUnpacker(options = {}) {
|
|
|
668
671
|
},
|
|
669
672
|
skipEntry() {
|
|
670
673
|
if (state !== STATE_BODY || !currentEntry) return true;
|
|
671
|
-
|
|
674
|
+
const toDiscard = Math.min(currentEntry.remaining, available());
|
|
675
|
+
if (toDiscard > 0) {
|
|
676
|
+
discard(toDiscard);
|
|
677
|
+
currentEntry.remaining -= toDiscard;
|
|
678
|
+
}
|
|
679
|
+
if (currentEntry.remaining > 0) return false;
|
|
672
680
|
return unpacker.skipPadding();
|
|
673
681
|
},
|
|
674
682
|
validateEOF() {
|
|
675
|
-
if (strict
|
|
676
|
-
if (
|
|
683
|
+
if (strict) {
|
|
684
|
+
if (!eof) throw truncateErr;
|
|
685
|
+
if (available() > 0) {
|
|
686
|
+
if (pull(available()).some((byte) => byte !== 0)) throw new Error("Invalid EOF.");
|
|
687
|
+
}
|
|
677
688
|
}
|
|
678
689
|
}
|
|
679
690
|
};
|
package/dist/web/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as normalizeBody, i as isBodyless, n as createTarPacker$1, r as transformHeader, t as createUnpacker } from "../unpacker-
|
|
1
|
+
import { a as normalizeBody, i as isBodyless, n as createTarPacker$1, r as transformHeader, t as createUnpacker } from "../unpacker-CEuY-276.js";
|
|
2
2
|
|
|
3
3
|
//#region src/web/compression.ts
|
|
4
4
|
function createGzipEncoder() {
|
|
@@ -71,6 +71,7 @@ async function streamToBuffer(stream) {
|
|
|
71
71
|
reader.releaseLock();
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
const drain = (stream) => stream.pipeTo(new WritableStream());
|
|
74
75
|
|
|
75
76
|
//#endregion
|
|
76
77
|
//#region src/web/unpack.ts
|
|
@@ -78,93 +79,45 @@ function createTarDecoder(options = {}) {
|
|
|
78
79
|
const unpacker = createUnpacker(options);
|
|
79
80
|
let bodyController = null;
|
|
80
81
|
let pumping = false;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const abortAll = (reason, controller) => {
|
|
84
|
-
if (controllerTerminated) return;
|
|
85
|
-
controllerTerminated = true;
|
|
86
|
-
const error = reason instanceof Error ? reason : new Error(String(reason ?? ""));
|
|
87
|
-
if (bodyController) {
|
|
88
|
-
try {
|
|
89
|
-
bodyController.error(error);
|
|
90
|
-
} catch {}
|
|
91
|
-
bodyController = null;
|
|
92
|
-
}
|
|
93
|
-
if (controller) try {
|
|
94
|
-
controller.error(error);
|
|
95
|
-
} catch {}
|
|
96
|
-
};
|
|
97
|
-
const pump = (controller, force = false) => {
|
|
98
|
-
if (pumping || controllerTerminated || eofReached) return;
|
|
82
|
+
const pump = (controller) => {
|
|
83
|
+
if (pumping) return;
|
|
99
84
|
pumping = true;
|
|
100
85
|
try {
|
|
101
|
-
while (
|
|
102
|
-
if (
|
|
103
|
-
if (!unpacker.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
86
|
+
while (true) if (unpacker.isEntryActive()) {
|
|
87
|
+
if (bodyController) {
|
|
88
|
+
if (unpacker.streamBody((c) => (bodyController.enqueue(c), true)) === 0 && !unpacker.isBodyComplete()) break;
|
|
89
|
+
} else if (!unpacker.skipEntry()) break;
|
|
90
|
+
if (unpacker.isBodyComplete()) {
|
|
91
|
+
try {
|
|
92
|
+
bodyController?.close();
|
|
93
|
+
} catch {}
|
|
94
|
+
bodyController = null;
|
|
95
|
+
if (!unpacker.skipPadding()) break;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
const header = unpacker.readHeader();
|
|
99
|
+
if (header === null || header === void 0) break;
|
|
100
|
+
controller.enqueue({
|
|
101
|
+
header,
|
|
102
|
+
body: new ReadableStream({
|
|
103
|
+
start(c) {
|
|
104
|
+
if (header.size === 0) c.close();
|
|
105
|
+
else bodyController = c;
|
|
106
|
+
},
|
|
113
107
|
pull: () => pump(controller),
|
|
114
|
-
cancel
|
|
108
|
+
cancel() {
|
|
115
109
|
bodyController = null;
|
|
116
110
|
pump(controller);
|
|
117
111
|
}
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
header,
|
|
121
|
-
body
|
|
122
|
-
});
|
|
123
|
-
if (header.size === 0) {
|
|
124
|
-
try {
|
|
125
|
-
bodyController.close();
|
|
126
|
-
} catch {}
|
|
127
|
-
bodyController = null;
|
|
128
|
-
if (!unpacker.skipPadding()) break;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (unpacker.isBodyComplete()) {
|
|
133
|
-
if (unpacker.skipPadding()) {
|
|
134
|
-
try {
|
|
135
|
-
bodyController.close();
|
|
136
|
-
} catch {}
|
|
137
|
-
bodyController = null;
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
if ((bodyController.desiredSize ?? 1) <= 0) break;
|
|
143
|
-
let shouldPause = false;
|
|
144
|
-
if (unpacker.streamBody((chunk) => {
|
|
145
|
-
if (!bodyController) return true;
|
|
146
|
-
try {
|
|
147
|
-
bodyController.enqueue(chunk);
|
|
148
|
-
if ((bodyController.desiredSize ?? 1) <= 0) shouldPause = true;
|
|
149
|
-
} catch {
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
return true;
|
|
153
|
-
}) === 0) break;
|
|
154
|
-
if (unpacker.isBodyComplete()) {
|
|
155
|
-
if (unpacker.skipPadding()) {
|
|
156
|
-
try {
|
|
157
|
-
bodyController.close();
|
|
158
|
-
} catch {}
|
|
159
|
-
bodyController = null;
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
if (shouldPause) break;
|
|
112
|
+
})
|
|
113
|
+
});
|
|
165
114
|
}
|
|
166
115
|
} catch (error) {
|
|
167
|
-
|
|
116
|
+
try {
|
|
117
|
+
bodyController?.error(error);
|
|
118
|
+
} catch {}
|
|
119
|
+
bodyController = null;
|
|
120
|
+
throw error;
|
|
168
121
|
} finally {
|
|
169
122
|
pumping = false;
|
|
170
123
|
}
|
|
@@ -172,29 +125,27 @@ function createTarDecoder(options = {}) {
|
|
|
172
125
|
return new TransformStream({
|
|
173
126
|
transform(chunk, controller) {
|
|
174
127
|
try {
|
|
175
|
-
if (eofReached && options.strict && chunk.some((byte) => byte !== 0)) throw new Error("Invalid EOF.");
|
|
176
128
|
unpacker.write(chunk);
|
|
177
129
|
pump(controller);
|
|
178
130
|
} catch (error) {
|
|
179
|
-
|
|
131
|
+
try {
|
|
132
|
+
bodyController?.error(error);
|
|
133
|
+
} catch {}
|
|
180
134
|
throw error;
|
|
181
135
|
}
|
|
182
136
|
},
|
|
183
137
|
flush(controller) {
|
|
184
138
|
try {
|
|
185
139
|
unpacker.end();
|
|
186
|
-
pump(controller
|
|
187
|
-
if (bodyController) {
|
|
188
|
-
if (options.strict) throw new Error("Tar archive is truncated.");
|
|
189
|
-
try {
|
|
190
|
-
bodyController.close();
|
|
191
|
-
} catch {}
|
|
192
|
-
bodyController = null;
|
|
193
|
-
}
|
|
140
|
+
pump(controller);
|
|
194
141
|
unpacker.validateEOF();
|
|
195
|
-
if (
|
|
142
|
+
if (unpacker.isEntryActive() && !unpacker.isBodyComplete()) try {
|
|
143
|
+
bodyController?.close();
|
|
144
|
+
} catch {}
|
|
196
145
|
} catch (error) {
|
|
197
|
-
|
|
146
|
+
try {
|
|
147
|
+
bodyController?.error(error);
|
|
148
|
+
} catch {}
|
|
198
149
|
throw error;
|
|
199
150
|
}
|
|
200
151
|
}
|
|
@@ -245,15 +196,12 @@ async function unpackTar(archive, options = {}) {
|
|
|
245
196
|
throw error;
|
|
246
197
|
}
|
|
247
198
|
if (processedHeader === null) {
|
|
248
|
-
await entry.body
|
|
199
|
+
await drain(entry.body);
|
|
249
200
|
continue;
|
|
250
201
|
}
|
|
251
202
|
if (isBodyless(processedHeader)) {
|
|
252
|
-
await entry.body
|
|
253
|
-
results.push({
|
|
254
|
-
header: processedHeader,
|
|
255
|
-
data: void 0
|
|
256
|
-
});
|
|
203
|
+
await drain(entry.body);
|
|
204
|
+
results.push({ header: processedHeader });
|
|
257
205
|
} else results.push({
|
|
258
206
|
header: processedHeader,
|
|
259
207
|
data: await streamToBuffer(entry.body)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modern-tar",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Zero dependency streaming tar parser and writer for JavaScript.",
|
|
5
5
|
"author": "Ayuhito <hello@ayuhito.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,12 +25,13 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@biomejs/biome": "2.3.
|
|
29
|
-
"@types/node": "^
|
|
30
|
-
"@vitest/
|
|
31
|
-
"
|
|
28
|
+
"@biomejs/biome": "2.3.8",
|
|
29
|
+
"@types/node": "^25.0.2",
|
|
30
|
+
"@vitest/browser-playwright": "4.0.15",
|
|
31
|
+
"@vitest/coverage-v8": "4.0.15",
|
|
32
|
+
"tsdown": "^0.18.0",
|
|
32
33
|
"typescript": "^5.9.3",
|
|
33
|
-
"vitest": "
|
|
34
|
+
"vitest": "4.0.15"
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"build": "tsdown",
|
|
@@ -38,7 +39,8 @@
|
|
|
38
39
|
"test": "vitest",
|
|
39
40
|
"coverage": "vitest run --coverage",
|
|
40
41
|
"check": "biome check --write",
|
|
41
|
-
"typecheck": "tsc --noEmit"
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"test:browser": "vitest --config=vitest.browser.config.ts --browser"
|
|
42
44
|
},
|
|
43
45
|
"files": [
|
|
44
46
|
"dist",
|