@openclaw/fs-safe 0.8.3 → 0.8.5
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/CHANGELOG.md +47 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +8 -7
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +15 -68
- package/dist/archive-plan.d.ts +20 -0
- package/dist/archive-plan.d.ts.map +1 -0
- package/dist/archive-plan.js +55 -0
- package/dist/archive-tar-inspect.d.ts +7 -0
- package/dist/archive-tar-inspect.d.ts.map +1 -0
- package/dist/archive-tar-inspect.js +59 -0
- package/dist/archive-tar.d.ts +3 -1
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +12 -49
- package/dist/archive.d.ts +1 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +20 -36
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +31 -4
- package/dist/directory-durability.js +5 -5
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +5 -6
- package/dist/file-store-boundary.js +1 -1
- package/dist/file-store-prune.js +17 -8
- package/dist/file-store.js +2 -2
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +5 -4
- package/dist/move-path.js +1 -1
- package/dist/native-binding.d.ts +2 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +6 -3
- package/dist/native-pinned-write.js +3 -3
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +12 -8
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +10 -9
- package/dist/path-policy.js +2 -2
- package/dist/pinned-write.d.ts +1 -0
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +15 -11
- package/dist/regular-file.js +8 -8
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +8 -4
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +13 -7
- package/dist/root-context.js +5 -5
- package/dist/root-impl.d.ts +2 -1
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +35 -26
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +2 -3
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +2 -3
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +3 -4
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +16 -15
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +5 -4
- package/dist/root-write-verification.js +6 -6
- package/dist/secret-file.js +2 -2
- package/dist/strict-file-identity.d.ts +1 -1
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +1 -2
- package/docs/archive.md +57 -0
- package/docs/atomic.md +2 -0
- package/docs/reading.md +2 -0
- package/docs/root.md +10 -1
- package/docs/types.md +2 -1
- package/docs/writing.md +26 -3
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.8.5 - 2026-09-07
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
- **Strict moves stay strict:** a move started with hardlink rejection keeps that policy throughout asynchronous preparation and copying, even when the caller reuses or changes its options object.
|
|
10
|
+
|
|
11
|
+
### Safe moves
|
|
12
|
+
|
|
13
|
+
- Preserve the admitted `sourceHardlinks` policy through staged-copy fallback. A late hardlink is still rejected before destination publication, with the source and its alias preserved, instead of being accepted after an in-flight options change.
|
|
14
|
+
- Document when move policy is captured and how it relates to the existing per-mutation authority checks and destination-publication receipts.
|
|
15
|
+
|
|
16
|
+
## 0.8.4 - 2026-09-07
|
|
17
|
+
|
|
18
|
+
### Highlights
|
|
19
|
+
|
|
20
|
+
- **Faster reads and writes:** metadata checks avoid unnecessary event-loop round-trips while data I/O stays asynchronous; reconstructible data can explicitly opt out of fsync with `durable: false`.
|
|
21
|
+
- **Inspect TARs without extracting:** the new bounded archive inspection API uses the same native/WASM admission and canonical planner as extraction.
|
|
22
|
+
|
|
23
|
+
### Filesystem performance and durability
|
|
24
|
+
|
|
25
|
+
- Run metadata checks inside async operations synchronously (microseconds each), cutting event-loop round-trips per read/write while data I/O remains asynchronous and native canonical path spelling is preserved, including Windows short paths.
|
|
26
|
+
- Add `durable` to Root write/create/writeJson/createJson/append options and Root defaults; `durable: false` skips file and parent fsync for reconstructible data (default unchanged: durable).
|
|
27
|
+
|
|
28
|
+
### Archive inspection and compatibility
|
|
29
|
+
|
|
30
|
+
- Add bounded `inspectTarArchive` with complete native/WASM admission and the same canonical extraction planner, preserving effective member identities without materializing an output tree.
|
|
31
|
+
- Preserve inherited and getter-backed extraction options on the WASM TAR path, matching native and ZIP handling for destinations, filters, stripping, and modes.
|
|
32
|
+
|
|
33
|
+
### Validation
|
|
34
|
+
|
|
35
|
+
- Apply the existing filesystem-test worker cap locally as well as in CI, and drain archive publication fixtures before resetting hooks or cleaning restricted directories after timeouts.
|
|
36
|
+
|
|
3
37
|
## 0.8.3 - 2026-09-06
|
|
4
38
|
|
|
39
|
+
### Highlights
|
|
40
|
+
|
|
41
|
+
- **Safer cancellable moves:** callers can guard every source removal and retain an exact destination-publication receipt for recovery after later failures, including Windows and cross-device fallbacks.
|
|
42
|
+
- **Less filesystem overhead:** reads and writes make fewer calls while preserving their existing boundary and identity checks.
|
|
43
|
+
|
|
44
|
+
### Move authority and recovery
|
|
45
|
+
|
|
5
46
|
- Add optional synchronous move authority checks before renames and each copied-source removal, plus an exact bigint destination-publication receipt for caller-owned recovery after later failure; retain cross-device and Windows `EPERM` copy fallbacks and the existing `Promise<void>` contract.
|
|
6
47
|
|
|
7
|
-
|
|
48
|
+
### Filesystem performance and durability
|
|
49
|
+
|
|
8
50
|
- Reduce filesystem calls per read and write while retaining boundary, hardlink, hook, retry, and post-mutation identity checks.
|
|
9
51
|
- Skip native publication's extra mode-only file fsync for modes that retain owner read/write when staging permissions have not widened; after a crash, a file may retain staged `0o600` instead of the wider requested mode, while restrictive modes retain the extra fsync.
|
|
52
|
+
|
|
53
|
+
### Tooling and maintenance
|
|
54
|
+
|
|
10
55
|
- Add 1 MiB read/write and existing-mode inheritance benchmarks, with native-mode metadata and per-case iteration counts.
|
|
56
|
+
- Simplify internals without changing public behavior: remove unused string/home helpers, `resolveUserPath`, `createBoundedReadStream`, `sidecarLockPayloadIsStale`, and `tarManifestEntryCost`; share filesystem utilities and merge private modules.
|
|
11
57
|
|
|
12
58
|
## 0.8.2 - 2026-09-05
|
|
13
59
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAIhE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAU5D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAmL7D,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWhE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF;AAuBD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,6BAA6B,CAAC,CA+ExC;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAazF;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,oBAAoB,CAAC,CAgB/B;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,4BAA4B,CAAC,CA2BvC"}
|
package/dist/absolute-path.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, } from "./directory-guard.js";
|
|
@@ -66,7 +67,7 @@ async function directoryGuardFailure(err, dir, scopeLabel) {
|
|
|
66
67
|
return { ok: false, code: err.code, error: err };
|
|
67
68
|
}
|
|
68
69
|
try {
|
|
69
|
-
const stat =
|
|
70
|
+
const stat = fsSync.lstatSync(dir);
|
|
70
71
|
const failure = classifyExistingDirectorySegment(stat, scopeLabel);
|
|
71
72
|
if (failure) {
|
|
72
73
|
return failure;
|
|
@@ -86,7 +87,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
|
|
|
86
87
|
let current = root;
|
|
87
88
|
let currentStat;
|
|
88
89
|
try {
|
|
89
|
-
currentStat =
|
|
90
|
+
currentStat = fsSync.lstatSync(current);
|
|
90
91
|
}
|
|
91
92
|
catch (err) {
|
|
92
93
|
const failure = classifyDirectoryLookupError(err, scopeLabel);
|
|
@@ -110,7 +111,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
|
|
|
110
111
|
}
|
|
111
112
|
const next = path.join(current, segment);
|
|
112
113
|
try {
|
|
113
|
-
const nextStat =
|
|
114
|
+
const nextStat = fsSync.lstatSync(next);
|
|
114
115
|
const segmentFailure = classifyExistingDirectorySegment(nextStat, scopeLabel);
|
|
115
116
|
if (segmentFailure) {
|
|
116
117
|
return segmentFailure;
|
|
@@ -155,7 +156,7 @@ async function findExistingAncestorWithStat(filePath) {
|
|
|
155
156
|
let current = path.resolve(filePath);
|
|
156
157
|
while (true) {
|
|
157
158
|
try {
|
|
158
|
-
return { path: current, stat:
|
|
159
|
+
return { path: current, stat: fsSync.lstatSync(current) };
|
|
159
160
|
}
|
|
160
161
|
catch (err) {
|
|
161
162
|
if (err.code !== "ENOENT") {
|
|
@@ -199,7 +200,7 @@ export async function ensureAbsoluteDirectory(dirPath, options = {}) {
|
|
|
199
200
|
return guardResult;
|
|
200
201
|
}
|
|
201
202
|
try {
|
|
202
|
-
const stat =
|
|
203
|
+
const stat = fsSync.lstatSync(current);
|
|
203
204
|
if (stat.isSymbolicLink()) {
|
|
204
205
|
return ensureDirectoryFailure("symlink", `directory path traverses a symlink within ${scopeLabel}`);
|
|
205
206
|
}
|
|
@@ -250,7 +251,7 @@ export async function canonicalPathFromExistingAncestor(filePath) {
|
|
|
250
251
|
}
|
|
251
252
|
let canonicalAncestor = ancestor;
|
|
252
253
|
try {
|
|
253
|
-
canonicalAncestor =
|
|
254
|
+
canonicalAncestor = fsSync.realpathSync.native(ancestor);
|
|
254
255
|
}
|
|
255
256
|
catch {
|
|
256
257
|
// Keep lexical path when the existing ancestor cannot be canonicalized.
|
|
@@ -263,7 +264,7 @@ export async function resolveAbsolutePathForRead(filePath, options = {}) {
|
|
|
263
264
|
const normalized = assertAbsolutePathInput(filePath);
|
|
264
265
|
let canonicalPath;
|
|
265
266
|
try {
|
|
266
|
-
canonicalPath =
|
|
267
|
+
canonicalPath = fsSync.realpathSync.native(normalized);
|
|
267
268
|
}
|
|
268
269
|
catch (err) {
|
|
269
270
|
if (err.code === "ENOENT") {
|
package/dist/archive-native.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ArchiveKind } from "./archive-kind.js";
|
|
|
3
3
|
import { type ResolvedArchiveExtractLimits, type TarMeterLimits } from "./archive-limits.js";
|
|
4
4
|
import type { ExtractArchiveOptions } from "./archive-options.js";
|
|
5
5
|
import type { NativeBinding } from "./native.js";
|
|
6
|
+
export declare function throwMappedNativeArchiveError(error: unknown): never;
|
|
6
7
|
export declare function extractNativeArchive(params: {
|
|
7
8
|
binding: NativeBinding;
|
|
8
9
|
archivePath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAOlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAYnE;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;CAClD,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FhB"}
|
package/dist/archive-native.js
CHANGED
|
@@ -2,22 +2,14 @@ import { classifyArchiveParserError } from "./archive-parser-errors.js";
|
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { stripArchivePath, validateArchiveEntryPath } from "./archive-entry.js";
|
|
6
|
+
import { createArchiveEntryPlanner } from "./archive-plan.js";
|
|
7
7
|
import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
8
|
-
import {
|
|
9
|
-
import { resolveArchiveEntryMode, shouldExtractArchiveEntry } from "./archive-policy.js";
|
|
8
|
+
import { assertArchiveEntryCountWithinLimit, } from "./archive-limits.js";
|
|
10
9
|
import { prepareArchiveDestinationDir, withStagedArchiveDestination, } from "./archive-staging.js";
|
|
11
10
|
import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
|
|
12
11
|
import { admitZipFile } from "./archive-zip-admission.js";
|
|
13
|
-
function
|
|
14
|
-
if (kind === "file" || kind === "directory")
|
|
15
|
-
return kind;
|
|
16
|
-
if (kind === "symlink" || kind === "hardlink")
|
|
17
|
-
return "symlink";
|
|
18
|
-
return "other";
|
|
19
|
-
}
|
|
20
|
-
function throwMappedNativeError(error) {
|
|
12
|
+
export function throwMappedNativeArchiveError(error) {
|
|
21
13
|
if (error instanceof Error) {
|
|
22
14
|
const mapped = classifyArchiveParserError(error.message, { cause: error });
|
|
23
15
|
if (mapped)
|
|
@@ -50,7 +42,7 @@ export async function extractNativeArchive(params) {
|
|
|
50
42
|
params.deadline.check();
|
|
51
43
|
const manifest = await params.binding
|
|
52
44
|
.inspectArchiveNative(stagedArchive.path, params.kind, tarLimits, params.deadline.signal)
|
|
53
|
-
.catch(
|
|
45
|
+
.catch(throwMappedNativeArchiveError);
|
|
54
46
|
params.deadline.check();
|
|
55
47
|
assertArchiveEntryCountWithinLimit(manifest.length, limits);
|
|
56
48
|
if (physicalCount !== undefined && manifest.length !== physicalCount) {
|
|
@@ -75,69 +67,24 @@ export async function extractNativeArchive(params) {
|
|
|
75
67
|
for (const entry of manifest)
|
|
76
68
|
validateArchiveEntryPath(entry.path);
|
|
77
69
|
}
|
|
78
|
-
const
|
|
79
|
-
const budget = createByteBudgetTracker(limits);
|
|
80
|
-
const trackOutputPath = createArchiveOutputPathTracker();
|
|
70
|
+
const planEntry = createArchiveEntryPlanner({ ...params, rootDir: stagingDir }, params.kind);
|
|
81
71
|
const plan = [];
|
|
82
72
|
for (const entry of manifest) {
|
|
83
73
|
params.deadline.check();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
|
|
93
|
-
trackOutputPath(relPath, entry.path);
|
|
94
|
-
resolveArchiveOutputPath({ rootDir: stagingDir, relPath, originalPath: entry.path });
|
|
95
|
-
const kind = policyKind(entry.kind);
|
|
96
|
-
if (!shouldExtractArchiveEntry({
|
|
97
|
-
filter: params.entryFilter,
|
|
98
|
-
onFiltered: params.onFiltered,
|
|
99
|
-
entry: { path: canonicalPath, kind, size: entry.size },
|
|
100
|
-
})) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (entry.kind === "sparse") {
|
|
104
|
-
throw new ArchiveFormatError(`GNU sparse archive entry is not supported: ${formatErrorDetail(entry.path)}`);
|
|
105
|
-
}
|
|
106
|
-
if (kind === "symlink" || entry.kind === "blocked") {
|
|
107
|
-
const label = params.kind === "zip" ? "zip" : "tar";
|
|
108
|
-
throw new ArchiveSecurityError("entry-link", `${label} entry is a link: ${formatErrorDetail(entry.path)}`);
|
|
109
|
-
}
|
|
110
|
-
if (!Number.isSafeInteger(entry.size) || entry.size < 0) {
|
|
111
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
|
|
112
|
-
}
|
|
113
|
-
// GNUDumpDir can carry a body; charge accepted TAR sizes as JS does.
|
|
114
|
-
if (kind === "file" || (kind === "directory" && params.kind !== "zip")) {
|
|
115
|
-
budget.startEntry();
|
|
116
|
-
budget.addEntrySize(entry.size);
|
|
117
|
-
}
|
|
118
|
-
if (kind === "file" || kind === "directory") {
|
|
119
|
-
plan.push({
|
|
120
|
-
index: entry.index,
|
|
121
|
-
path: relPath,
|
|
122
|
-
kind,
|
|
123
|
-
size: entry.size,
|
|
124
|
-
mode: resolveArchiveEntryMode({
|
|
125
|
-
kind,
|
|
126
|
-
archivedMode: params.kind === "zip"
|
|
127
|
-
? zipEntries[entry.index].creatorSystem === 3
|
|
128
|
-
? zipEntries[entry.index].externalAttributes >>> 16
|
|
129
|
-
: undefined
|
|
130
|
-
: entry.mode,
|
|
131
|
-
policy: params.entryModes,
|
|
132
|
-
}),
|
|
133
|
-
});
|
|
134
|
-
}
|
|
74
|
+
const mode = params.kind === "zip"
|
|
75
|
+
? zipEntries[entry.index].creatorSystem === 3
|
|
76
|
+
? zipEntries[entry.index].externalAttributes >>> 16
|
|
77
|
+
: undefined
|
|
78
|
+
: entry.mode;
|
|
79
|
+
const accepted = planEntry({ ...entry, mode });
|
|
80
|
+
if (accepted)
|
|
81
|
+
plan.push({ ...accepted, index: entry.index });
|
|
135
82
|
}
|
|
136
83
|
const directory = await fs.open(stagingDir, fsConstants.O_RDONLY |
|
|
137
84
|
(typeof fsConstants.O_DIRECTORY === "number" ? fsConstants.O_DIRECTORY : 0));
|
|
138
85
|
try {
|
|
139
86
|
params.deadline.check();
|
|
140
|
-
await params.binding.extractArchiveNative(stagedArchive.path, params.kind, directory.fd, plan.map((entry) => ({ ...entry, mode: entry.kind === "directory" ? 0o700 : 0o600 })), tarLimits, params.deadline.signal).catch(
|
|
87
|
+
await params.binding.extractArchiveNative(stagedArchive.path, params.kind, directory.fd, plan.map((entry) => ({ ...entry, mode: entry.kind === "directory" ? 0o700 : 0o600 })), tarLimits, params.deadline.signal).catch(throwMappedNativeArchiveError);
|
|
141
88
|
}
|
|
142
89
|
finally {
|
|
143
90
|
await directory.close().catch(() => undefined);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ArchiveKind } from "./archive-kind.js";
|
|
2
|
+
import type { ExtractArchiveOptions } from "./archive-options.js";
|
|
3
|
+
export type ArchiveMemberKind = "file" | "directory" | "symlink" | "hardlink" | "blocked" | "sparse" | "other";
|
|
4
|
+
export type ArchivePlanEntry = {
|
|
5
|
+
path: string;
|
|
6
|
+
kind: "file" | "directory";
|
|
7
|
+
size: number;
|
|
8
|
+
mode: number;
|
|
9
|
+
};
|
|
10
|
+
export type ArchivePlanOptions = Pick<ExtractArchiveOptions, "stripComponents" | "limits" | "entryModes" | "entryFilter" | "onFiltered"> & {
|
|
11
|
+
rootDir?: string;
|
|
12
|
+
escapeLabel?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function createArchiveEntryPlanner(params: ArchivePlanOptions, archiveKind: ArchiveKind): (entry: {
|
|
15
|
+
path: string;
|
|
16
|
+
kind: ArchiveMemberKind;
|
|
17
|
+
size: number;
|
|
18
|
+
mode?: number;
|
|
19
|
+
}) => ArchivePlanEntry | null;
|
|
20
|
+
//# sourceMappingURL=archive-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-plan.d.ts","sourceRoot":"","sources":["../src/archive-plan.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC/G,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACxG,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,EACzD,iBAAiB,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC,GAAG;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAIF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,GAAG,CAAC,KAAK,EAAE;IACvG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACpE,KAAK,gBAAgB,GAAG,IAAI,CA2C5B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
|
+
import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
3
|
+
import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, resolveExtractLimits, } from "./archive-limits.js";
|
|
4
|
+
import { resolveArchiveEntryMode, shouldExtractArchiveEntry } from "./archive-policy.js";
|
|
5
|
+
import { formatErrorDetail } from "./error-detail.js";
|
|
6
|
+
// Inspection and both executors decide over the same admitted identities.
|
|
7
|
+
// A destination is optional only for inspection; writers still prove containment.
|
|
8
|
+
export function createArchiveEntryPlanner(params, archiveKind) {
|
|
9
|
+
const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
|
|
10
|
+
const limits = resolveExtractLimits(params.limits);
|
|
11
|
+
const budget = createByteBudgetTracker(limits);
|
|
12
|
+
const trackOutputPath = createArchiveOutputPathTracker();
|
|
13
|
+
let entryCount = 0;
|
|
14
|
+
return (entry) => {
|
|
15
|
+
assertArchiveEntryCountWithinLimit(++entryCount, limits);
|
|
16
|
+
validateArchiveEntryPath(entry.path, { escapeLabel: params.escapeLabel });
|
|
17
|
+
const canonicalPath = stripArchivePath(entry.path, 0);
|
|
18
|
+
if (!canonicalPath)
|
|
19
|
+
return null;
|
|
20
|
+
const relPath = stripArchivePath(canonicalPath, strip);
|
|
21
|
+
if (!relPath)
|
|
22
|
+
return null;
|
|
23
|
+
validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
|
|
24
|
+
assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
|
|
25
|
+
trackOutputPath(relPath, entry.path);
|
|
26
|
+
if (params.rootDir !== undefined) {
|
|
27
|
+
resolveArchiveOutputPath({ rootDir: params.rootDir, relPath, originalPath: entry.path,
|
|
28
|
+
escapeLabel: params.escapeLabel });
|
|
29
|
+
}
|
|
30
|
+
const kind = entry.kind === "file" || entry.kind === "directory" ? entry.kind
|
|
31
|
+
: entry.kind === "symlink" || entry.kind === "hardlink" ? "symlink" : "other";
|
|
32
|
+
if (!shouldExtractArchiveEntry({ filter: params.entryFilter, onFiltered: params.onFiltered,
|
|
33
|
+
entry: { path: canonicalPath, kind, size: entry.size } }))
|
|
34
|
+
return null;
|
|
35
|
+
if (entry.kind === "sparse") {
|
|
36
|
+
throw new ArchiveFormatError(`GNU sparse archive entry is not supported: ${formatErrorDetail(entry.path)}`);
|
|
37
|
+
}
|
|
38
|
+
if (kind === "symlink" || entry.kind === "blocked") {
|
|
39
|
+
const label = archiveKind === "zip" ? "zip" : "tar";
|
|
40
|
+
throw new ArchiveSecurityError("entry-link", `${label} entry is a link: ${formatErrorDetail(entry.path)}`);
|
|
41
|
+
}
|
|
42
|
+
if (!Number.isSafeInteger(entry.size) || entry.size < 0) {
|
|
43
|
+
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
|
|
44
|
+
}
|
|
45
|
+
// Unsupported records remain visible to policy but cannot create outputs.
|
|
46
|
+
if (kind === "other")
|
|
47
|
+
return null;
|
|
48
|
+
if (kind === "file" || archiveKind !== "zip") {
|
|
49
|
+
budget.startEntry();
|
|
50
|
+
budget.addEntrySize(entry.size);
|
|
51
|
+
}
|
|
52
|
+
return { path: relPath, kind, size: entry.size,
|
|
53
|
+
mode: resolveArchiveEntryMode({ kind, archivedMode: entry.mode, policy: params.entryModes }) };
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExtractArchiveOptions } from "./archive-options.js";
|
|
2
|
+
import { type ArchivePlanEntry } from "./archive-plan.js";
|
|
3
|
+
export type InspectTarArchiveOptions = Pick<ExtractArchiveOptions, "archivePath" | "timeoutMs" | "limits" | "entryFilter" | "onFiltered">;
|
|
4
|
+
export type InspectedTarEntry = Readonly<Pick<ArchivePlanEntry, "path" | "kind" | "size">>;
|
|
5
|
+
/** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
|
|
6
|
+
export declare function inspectTarArchive(params: InspectTarArchiveOptions): Promise<readonly InspectedTarEntry[]>;
|
|
7
|
+
//# sourceMappingURL=archive-tar-inspect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-tar-inspect.d.ts","sourceRoot":"","sources":["../src/archive-tar-inspect.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAOrF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,EAC/D,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AAE3F,2FAA2F;AAC3F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA2C/G"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { withExtractionDeadline } from "./archive-deadline.js";
|
|
2
|
+
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
+
import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
4
|
+
import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
|
|
5
|
+
import { throwMappedNativeArchiveError } from "./archive-native.js";
|
|
6
|
+
import { createArchiveEntryPlanner } from "./archive-plan.js";
|
|
7
|
+
import { resolveArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
8
|
+
import { createTarEntryPlanner } from "./archive-tar.js";
|
|
9
|
+
import { inspectTar } from "./archive-tar-stream.js";
|
|
10
|
+
import { getNativeBinding } from "./native.js";
|
|
11
|
+
/** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
|
|
12
|
+
export async function inspectTarArchive(params) {
|
|
13
|
+
const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
|
|
14
|
+
const limits = resolveExtractLimits(params.limits);
|
|
15
|
+
const tarLimits = resolveTarMeterLimits(limits);
|
|
16
|
+
const native = getNativeBinding();
|
|
17
|
+
return await withExtractionDeadline(params.timeoutMs, "inspect tar", async (deadline) => {
|
|
18
|
+
const staged = await stageArchiveFileForExtraction({ archivePath: params.archivePath, limits, deadline });
|
|
19
|
+
try {
|
|
20
|
+
// Staging closes descriptors asynchronously; expiry there must not start a decoder.
|
|
21
|
+
deadline.check();
|
|
22
|
+
const entries = [];
|
|
23
|
+
const append = (entry) => {
|
|
24
|
+
if (entry)
|
|
25
|
+
entries.push(Object.freeze({ path: entry.path, kind: entry.kind, size: entry.size }));
|
|
26
|
+
};
|
|
27
|
+
const policy = { limits, entryFilter: params.entryFilter, onFiltered };
|
|
28
|
+
if (native) {
|
|
29
|
+
const manifest = await native.inspectArchiveNative(staged.path, "tar", tarLimits, deadline.signal)
|
|
30
|
+
.catch(throwMappedNativeArchiveError);
|
|
31
|
+
deadline.check();
|
|
32
|
+
// Match extraction's whole-manifest validation before caller policy runs.
|
|
33
|
+
for (const entry of manifest)
|
|
34
|
+
validateArchiveEntryPath(entry.path);
|
|
35
|
+
const planEntry = createArchiveEntryPlanner(policy, "tar");
|
|
36
|
+
for (const entry of manifest) {
|
|
37
|
+
deadline.check();
|
|
38
|
+
append(planEntry(entry));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const manifest = [];
|
|
43
|
+
await inspectTar({ archivePath: staged.path, limits: tarLimits, signal: deadline.signal,
|
|
44
|
+
onMember: (entry) => { manifest.push(entry); } });
|
|
45
|
+
const planEntry = createTarEntryPlanner(policy);
|
|
46
|
+
for (const entry of manifest) {
|
|
47
|
+
deadline.check();
|
|
48
|
+
append(planEntry(entry));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
deadline.check();
|
|
52
|
+
// This is bounded evidence about the staged bytes, not a reusable write plan.
|
|
53
|
+
return Object.freeze(entries);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await staged.cleanup();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
package/dist/archive-tar.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ArchiveExtractLimits } from "./archive-limits.js";
|
|
2
|
+
import { type ArchivePlanEntry, type ArchivePlanOptions } from "./archive-plan.js";
|
|
2
3
|
import { type ArchiveEntryFilter, type ArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
3
4
|
export type TarEntryInfo = {
|
|
4
5
|
path: string;
|
|
@@ -6,6 +7,7 @@ export type TarEntryInfo = {
|
|
|
6
7
|
size: number;
|
|
7
8
|
mode?: number;
|
|
8
9
|
};
|
|
10
|
+
export declare function createTarEntryPlanner(params: ArchivePlanOptions): (entry: TarEntryInfo) => ArchivePlanEntry | null;
|
|
9
11
|
export declare function createTarEntryPreflightChecker(params: {
|
|
10
12
|
rootDir: string;
|
|
11
13
|
stripComponents?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AASvF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAC9D,CAAC,KAAK,EAAE,YAAY,KAAK,gBAAgB,GAAG,IAAI,CAQjD;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAGnC"}
|
package/dist/archive-tar.js
CHANGED
|
@@ -1,58 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { archiveEntryKindFromTarType, shouldExtractArchiveEntry, } from "./archive-policy.js";
|
|
4
|
-
import { ArchiveSecurityError } from "./archive-errors.js";
|
|
5
|
-
import { formatErrorDetail } from "./error-detail.js";
|
|
1
|
+
import { createArchiveEntryPlanner, } from "./archive-plan.js";
|
|
2
|
+
import { archiveEntryKindFromTarType, } from "./archive-policy.js";
|
|
6
3
|
const BLOCKED_TAR_ENTRY_TYPES = new Set([
|
|
7
|
-
"SymbolicLink",
|
|
8
|
-
"Link",
|
|
9
4
|
"BlockDevice",
|
|
10
5
|
"CharacterDevice",
|
|
11
6
|
"FIFO",
|
|
12
7
|
"Socket",
|
|
13
8
|
]);
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
let entryCount = 0;
|
|
18
|
-
const budget = createByteBudgetTracker(limits);
|
|
19
|
-
const trackOutputPath = createArchiveOutputPathTracker();
|
|
9
|
+
export function createTarEntryPlanner(params) {
|
|
10
|
+
// Preserve inherited/getter-backed public options and per-entry policy reads.
|
|
11
|
+
const plan = createArchiveEntryPlanner(params, "tar");
|
|
20
12
|
return (entry) => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const canonicalPath = stripArchivePath(entry.path, 0);
|
|
25
|
-
if (!canonicalPath)
|
|
26
|
-
return false;
|
|
27
|
-
const relPath = stripArchivePath(canonicalPath, strip);
|
|
28
|
-
if (!relPath) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
|
|
32
|
-
assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
|
|
33
|
-
trackOutputPath(relPath, entry.path);
|
|
34
|
-
resolveArchiveOutputPath({
|
|
35
|
-
rootDir: params.rootDir,
|
|
36
|
-
relPath,
|
|
37
|
-
originalPath: entry.path,
|
|
38
|
-
escapeLabel: params.escapeLabel,
|
|
39
|
-
});
|
|
40
|
-
const kind = archiveEntryKindFromTarType(entry.type);
|
|
41
|
-
if (!shouldExtractArchiveEntry({
|
|
42
|
-
filter: params.entryFilter,
|
|
43
|
-
onFiltered: params.onFiltered,
|
|
44
|
-
entry: { path: canonicalPath, kind, size: entry.size },
|
|
45
|
-
})) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
if (BLOCKED_TAR_ENTRY_TYPES.has(entry.type)) {
|
|
49
|
-
throw new ArchiveSecurityError("entry-link", `tar entry is a link: ${formatErrorDetail(entry.path)}`);
|
|
50
|
-
}
|
|
51
|
-
// Accepted unsupported records remain omitted, as in the native plan.
|
|
52
|
-
if (kind === "other")
|
|
53
|
-
return false;
|
|
54
|
-
budget.startEntry();
|
|
55
|
-
budget.addEntrySize(entry.size);
|
|
56
|
-
return true;
|
|
13
|
+
const kind = BLOCKED_TAR_ENTRY_TYPES.has(entry.type) ? "blocked" : archiveEntryKindFromTarType(entry.type);
|
|
14
|
+
// The public checker accepts structural entry objects, including class getters.
|
|
15
|
+
return plan({ path: entry.path, kind, size: entry.size, mode: entry.mode });
|
|
57
16
|
};
|
|
58
17
|
}
|
|
18
|
+
export function createTarEntryPreflightChecker(params) {
|
|
19
|
+
const plan = createTarEntryPlanner(params);
|
|
20
|
+
return (entry) => plan(entry) !== null;
|
|
21
|
+
}
|
package/dist/archive.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type { ArchiveEntryFilter, ArchiveEntryKind, ArchiveEntryModePolicy, Arch
|
|
|
4
4
|
export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
5
5
|
export { resolveArchiveKind, resolvePackedRootDir, type ArchiveKind } from "./archive-kind.js";
|
|
6
6
|
export { readArchiveEntry } from "./archive-read.js";
|
|
7
|
+
export { inspectTarArchive, type InspectTarArchiveOptions, type InspectedTarEntry } from "./archive-tar-inspect.js";
|
|
7
8
|
export { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, DEFAULT_MAX_ARCHIVE_BYTES_ZIP, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_EXTRACTED_BYTES, DEFAULT_MAX_ENTRY_BYTES, DEFAULT_MAX_META_ENTRY_BYTES, DEFAULT_MAX_ENTRY_PATH_COMPONENTS, type ArchiveExtractLimits, type ArchiveLimitErrorCode, } from "./archive-limits.js";
|
|
8
9
|
export { ArchiveFormatError, type ArchiveFormatErrorCode } from "./archive-errors.js";
|
|
9
10
|
export { ArchiveSecurityError, type ArchiveSecurityErrorCode } from "./archive-staging.js";
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AA6MpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CjF"}
|