@foss.global/gitmanager 1.7.1 → 2.1.0
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/.smartconfig.json +4 -1
- package/changelog.md +52 -0
- package/dist_rust/rustgitmanager_linux_amd64 +0 -0
- package/dist_rust/rustgitmanager_linux_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustgitmanager_linux_arm64 +0 -0
- package/dist_rust/rustgitmanager_linux_arm64.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.archiveinspection.d.ts +11 -0
- package/dist_ts/classes.archiveinspection.js +205 -0
- package/dist_ts/classes.gitmanager.d.ts +26 -2
- package/dist_ts/classes.gitmanager.js +60 -1
- package/dist_ts/classes.gitmanagererror.d.ts +11 -0
- package/dist_ts/classes.gitmanagererror.js +22 -0
- package/dist_ts/classes.repositoryservice.d.ts +8 -0
- package/dist_ts/classes.repositoryservice.js +38 -12
- package/dist_ts/index.d.ts +2 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/models.d.ts +66 -1
- package/dist_ts/rust-gitmanager-bridge.d.ts +6 -2
- package/dist_ts/rust-gitmanager-bridge.js +41 -2
- package/native-notices/crates/adler2-2.0.1/license-0bsd +12 -0
- package/native-notices/crates/adler2-2.0.1/license-apache +201 -0
- package/native-notices/crates/adler2-2.0.1/license-mit +23 -0
- package/native-notices/crates/aho-corasick-1.1.4/copying +3 -0
- package/native-notices/crates/aho-corasick-1.1.4/license-mit +21 -0
- package/native-notices/crates/aho-corasick-1.1.4/unlicense +24 -0
- package/native-notices/crates/anstream-1.0.0/license-apache +202 -0
- package/native-notices/crates/anstream-1.0.0/license-mit +19 -0
- package/native-notices/crates/anstyle-1.0.14/license-apache +202 -0
- package/native-notices/crates/anstyle-1.0.14/license-mit +19 -0
- package/native-notices/crates/anstyle-parse-1.0.0/license-apache +202 -0
- package/native-notices/crates/anstyle-parse-1.0.0/license-mit +19 -0
- package/native-notices/crates/anstyle-query-1.1.5/license-apache +202 -0
- package/native-notices/crates/anstyle-query-1.1.5/license-mit +19 -0
- package/native-notices/crates/anyhow-1.0.102/license-apache +176 -0
- package/native-notices/crates/anyhow-1.0.102/license-mit +23 -0
- package/native-notices/crates/atomic-waker-1.1.2/license-apache +201 -0
- package/native-notices/crates/atomic-waker-1.1.2/license-mit +23 -0
- package/native-notices/crates/atomic-waker-1.1.2/license-third-party +45 -0
- package/native-notices/crates/axum-0.8.9/license +25 -0
- package/native-notices/crates/axum-core-0.5.6/license +27 -0
- package/native-notices/crates/base64-0.22.1/license-apache +201 -0
- package/native-notices/crates/base64-0.22.1/license-mit +21 -0
- package/native-notices/crates/block-buffer-0.10.4/license-apache +201 -0
- package/native-notices/crates/block-buffer-0.10.4/license-mit +25 -0
- package/native-notices/crates/bytes-1.12.0/license +25 -0
- package/native-notices/crates/cfg-if-1.0.4/license-apache +201 -0
- package/native-notices/crates/cfg-if-1.0.4/license-mit +25 -0
- package/native-notices/crates/clap-4.6.1/license-apache +202 -0
- package/native-notices/crates/clap-4.6.1/license-mit +19 -0
- package/native-notices/crates/clap_builder-4.6.0/license-apache +202 -0
- package/native-notices/crates/clap_builder-4.6.0/license-mit +19 -0
- package/native-notices/crates/clap_derive-4.6.1/license-apache +202 -0
- package/native-notices/crates/clap_derive-4.6.1/license-mit +19 -0
- package/native-notices/crates/clap_lex-1.1.0/license-apache +202 -0
- package/native-notices/crates/clap_lex-1.1.0/license-mit +19 -0
- package/native-notices/crates/colorchoice-1.0.5/license-apache +202 -0
- package/native-notices/crates/colorchoice-1.0.5/license-mit +19 -0
- package/native-notices/crates/cpufeatures-0.2.17/license-apache +201 -0
- package/native-notices/crates/cpufeatures-0.2.17/license-mit +25 -0
- package/native-notices/crates/crc32fast-1.5.0/license-apache +202 -0
- package/native-notices/crates/crc32fast-1.5.0/license-mit +21 -0
- package/native-notices/crates/crypto-common-0.1.7/license-apache +201 -0
- package/native-notices/crates/crypto-common-0.1.7/license-mit +25 -0
- package/native-notices/crates/digest-0.10.7/license-apache +201 -0
- package/native-notices/crates/digest-0.10.7/license-mit +25 -0
- package/native-notices/crates/errno-0.3.14/license-apache +201 -0
- package/native-notices/crates/errno-0.3.14/license-mit +25 -0
- package/native-notices/crates/flate2-1.1.9/license-apache +201 -0
- package/native-notices/crates/flate2-1.1.9/license-mit +25 -0
- package/native-notices/crates/form_urlencoded-1.2.2/license-apache +201 -0
- package/native-notices/crates/form_urlencoded-1.2.2/license-mit +25 -0
- package/native-notices/crates/futures-channel-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-channel-0.3.33/license-mit +26 -0
- package/native-notices/crates/futures-core-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-core-0.3.33/license-mit +26 -0
- package/native-notices/crates/futures-macro-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-macro-0.3.33/license-mit +26 -0
- package/native-notices/crates/futures-sink-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-sink-0.3.33/license-mit +26 -0
- package/native-notices/crates/futures-task-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-task-0.3.33/license-mit +26 -0
- package/native-notices/crates/futures-util-0.3.33/license-apache +202 -0
- package/native-notices/crates/futures-util-0.3.33/license-mit +26 -0
- package/native-notices/crates/generic-array-0.14.7/license +21 -0
- package/native-notices/crates/heck-0.5.0/license-apache +201 -0
- package/native-notices/crates/heck-0.5.0/license-mit +25 -0
- package/native-notices/crates/http-1.4.2/license-apache +201 -0
- package/native-notices/crates/http-1.4.2/license-mit +25 -0
- package/native-notices/crates/http-body-1.1.0/license +25 -0
- package/native-notices/crates/http-body-util-0.1.4/license +25 -0
- package/native-notices/crates/httparse-1.10.1/license-apache +201 -0
- package/native-notices/crates/httparse-1.10.1/license-mit +20 -0
- package/native-notices/crates/httpdate-1.0.3/license-apache +201 -0
- package/native-notices/crates/httpdate-1.0.3/license-mit +19 -0
- package/native-notices/crates/hyper-1.10.1/license +19 -0
- package/native-notices/crates/hyper-util-0.1.20/license +19 -0
- package/native-notices/crates/is_terminal_polyfill-1.70.2/license-apache +202 -0
- package/native-notices/crates/is_terminal_polyfill-1.70.2/license-mit +19 -0
- package/native-notices/crates/itoa-1.0.18/license-apache +176 -0
- package/native-notices/crates/itoa-1.0.18/license-mit +23 -0
- package/native-notices/crates/lazy_static-1.5.0/license-apache +201 -0
- package/native-notices/crates/lazy_static-1.5.0/license-mit +25 -0
- package/native-notices/crates/libc-0.2.186/license-apache +176 -0
- package/native-notices/crates/libc-0.2.186/license-mit +25 -0
- package/native-notices/crates/lock_api-0.4.14/license-apache +201 -0
- package/native-notices/crates/lock_api-0.4.14/license-mit +25 -0
- package/native-notices/crates/log-0.4.33/license-apache +201 -0
- package/native-notices/crates/log-0.4.33/license-mit +25 -0
- package/native-notices/crates/matchers-0.2.0/license +19 -0
- package/native-notices/crates/matchit-0.8.4/license +21 -0
- package/native-notices/crates/matchit-0.8.4/license.httprouter +29 -0
- package/native-notices/crates/memchr-2.8.2/copying +3 -0
- package/native-notices/crates/memchr-2.8.2/license-mit +21 -0
- package/native-notices/crates/memchr-2.8.2/unlicense +24 -0
- package/native-notices/crates/mime-0.3.17/license-apache +201 -0
- package/native-notices/crates/mime-0.3.17/license-mit +20 -0
- package/native-notices/crates/miniz_oxide-0.8.9/license +25 -0
- package/native-notices/crates/miniz_oxide-0.8.9/license-apache.md +177 -0
- package/native-notices/crates/miniz_oxide-0.8.9/license-mit.md +24 -0
- package/native-notices/crates/miniz_oxide-0.8.9/license-zlib.md +14 -0
- package/native-notices/crates/mio-1.2.1/license +19 -0
- package/native-notices/crates/nu-ansi-term-0.50.3/license +22 -0
- package/native-notices/crates/once_cell-1.21.4/license-apache +201 -0
- package/native-notices/crates/once_cell-1.21.4/license-mit +23 -0
- package/native-notices/crates/parking_lot-0.12.5/license-apache +201 -0
- package/native-notices/crates/parking_lot-0.12.5/license-mit +25 -0
- package/native-notices/crates/parking_lot_core-0.9.12/license-apache +201 -0
- package/native-notices/crates/parking_lot_core-0.9.12/license-mit +25 -0
- package/native-notices/crates/percent-encoding-2.3.2/license-apache +201 -0
- package/native-notices/crates/percent-encoding-2.3.2/license-mit +25 -0
- package/native-notices/crates/pin-project-lite-0.2.17/license-apache +177 -0
- package/native-notices/crates/pin-project-lite-0.2.17/license-mit +23 -0
- package/native-notices/crates/proc-macro2-1.0.106/license-apache +176 -0
- package/native-notices/crates/proc-macro2-1.0.106/license-mit +23 -0
- package/native-notices/crates/quote-1.0.45/license-apache +176 -0
- package/native-notices/crates/quote-1.0.45/license-mit +23 -0
- package/native-notices/crates/regex-automata-0.4.14/license-apache +201 -0
- package/native-notices/crates/regex-automata-0.4.14/license-mit +25 -0
- package/native-notices/crates/regex-syntax-0.8.11/license-apache +201 -0
- package/native-notices/crates/regex-syntax-0.8.11/license-mit +25 -0
- package/native-notices/crates/ryu-1.0.23/license-apache +176 -0
- package/native-notices/crates/ryu-1.0.23/license-boost +23 -0
- package/native-notices/crates/scopeguard-1.2.0/license-apache +201 -0
- package/native-notices/crates/scopeguard-1.2.0/license-mit +25 -0
- package/native-notices/crates/serde-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_core-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde_core-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_derive-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde_derive-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_json-1.0.150/license-apache +176 -0
- package/native-notices/crates/serde_json-1.0.150/license-mit +23 -0
- package/native-notices/crates/serde_path_to_error-0.1.20/license-apache +176 -0
- package/native-notices/crates/serde_path_to_error-0.1.20/license-mit +23 -0
- package/native-notices/crates/serde_urlencoded-0.7.1/license-apache +176 -0
- package/native-notices/crates/serde_urlencoded-0.7.1/license-mit +25 -0
- package/native-notices/crates/sha2-0.10.9/license-apache +201 -0
- package/native-notices/crates/sha2-0.10.9/license-mit +27 -0
- package/native-notices/crates/sharded-slab-0.1.7/license +19 -0
- package/native-notices/crates/signal-hook-registry-1.4.8/license-apache +201 -0
- package/native-notices/crates/signal-hook-registry-1.4.8/license-mit +25 -0
- package/native-notices/crates/simd-adler32-0.3.10/license.md +21 -0
- package/native-notices/crates/slab-0.4.12/license +25 -0
- package/native-notices/crates/smallvec-1.15.2/license-apache +201 -0
- package/native-notices/crates/smallvec-1.15.2/license-mit +25 -0
- package/native-notices/crates/socket2-0.6.4/license-apache +201 -0
- package/native-notices/crates/socket2-0.6.4/license-mit +25 -0
- package/native-notices/crates/strsim-0.11.1/license +23 -0
- package/native-notices/crates/syn-2.0.118/license-apache +176 -0
- package/native-notices/crates/syn-2.0.118/license-mit +23 -0
- package/native-notices/crates/sync_wrapper-1.0.2/license +177 -0
- package/native-notices/crates/thread_local-1.1.9/license-apache +201 -0
- package/native-notices/crates/thread_local-1.1.9/license-mit +25 -0
- package/native-notices/crates/tokio-1.52.3/license +21 -0
- package/native-notices/crates/tokio-macros-2.7.0/license +22 -0
- package/native-notices/crates/tokio-stream-0.1.18/license +21 -0
- package/native-notices/crates/tokio-util-0.7.18/license +21 -0
- package/native-notices/crates/tower-0.5.3/license +25 -0
- package/native-notices/crates/tower-layer-0.3.3/license +25 -0
- package/native-notices/crates/tower-service-0.3.3/license +25 -0
- package/native-notices/crates/tracing-0.1.44/license +25 -0
- package/native-notices/crates/tracing-attributes-0.1.31/license +25 -0
- package/native-notices/crates/tracing-core-0.1.36/license +25 -0
- package/native-notices/crates/tracing-log-0.2.0/license +25 -0
- package/native-notices/crates/tracing-subscriber-0.3.23/license +25 -0
- package/native-notices/crates/typenum-1.20.1/license +1 -0
- package/native-notices/crates/typenum-1.20.1/license-apache +201 -0
- package/native-notices/crates/typenum-1.20.1/license-mit +21 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-apache +176 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-mit +23 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-unicode +39 -0
- package/native-notices/crates/utf8parse-0.2.2/license-apache +176 -0
- package/native-notices/crates/utf8parse-0.2.2/license-mit +25 -0
- package/native-notices/crates/version_check-0.9.5/license-apache +201 -0
- package/native-notices/crates/version_check-0.9.5/license-mit +19 -0
- package/native-notices/crates/zmij-1.0.21/license-mit +23 -0
- package/native-notices/manifest.json +2457 -0
- package/native-notices/readme.md +44 -0
- package/native-notices/runtime/gcc/gpl-3.0.txt +674 -0
- package/native-notices/runtime/gcc/runtime-library-exception-3.1.txt +73 -0
- package/native-notices/runtime/glibc/lgpl-2.1.txt +502 -0
- package/native-notices/rust/compiler-builtins-license.txt +275 -0
- package/native-notices/rust/copyright-library.html +8266 -0
- package/native-notices/rust/crates/addr2line-0.25.1/license-apache +201 -0
- package/native-notices/rust/crates/addr2line-0.25.1/license-mit +25 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-0bsd +12 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-apache +201 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-mit +23 -0
- package/native-notices/rust/crates/memchr-2.7.6/copying +3 -0
- package/native-notices/rust/crates/memchr-2.7.6/license-mit +21 -0
- package/native-notices/rust/crates/memchr-2.7.6/unlicense +24 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license +25 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-apache.md +177 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-mit.md +24 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-zlib.md +14 -0
- package/native-notices/rust/crates/object-0.37.3/license-apache +201 -0
- package/native-notices/rust/crates/object-0.37.3/license-mit +25 -0
- package/native-notices/rust/licenses/apache-2.0.txt +73 -0
- package/native-notices/rust/licenses/bsd-2-clause.txt +9 -0
- package/native-notices/rust/licenses/cc-by-sa-4.0.txt +427 -0
- package/native-notices/rust/licenses/gcc-exception-3.1.txt +30 -0
- package/native-notices/rust/licenses/gpl-2.0-only.txt +133 -0
- package/native-notices/rust/licenses/gpl-3.0-or-later.txt +202 -0
- package/native-notices/rust/licenses/isc.txt +7 -0
- package/native-notices/rust/licenses/llvm-exception.txt +15 -0
- package/native-notices/rust/licenses/mit.txt +9 -0
- package/native-notices/rust/licenses/ncsa.txt +29 -0
- package/native-notices/rust/licenses/ofl-1.1.txt +43 -0
- package/native-notices/rust/licenses/unicode-3.0.txt +39 -0
- package/package.json +10 -9
- package/readme.md +100 -7
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.archiveinspection.ts +245 -0
- package/ts/classes.gitmanager.ts +72 -2
- package/ts/classes.gitmanagererror.ts +28 -0
- package/ts/classes.repositoryservice.ts +41 -12
- package/ts/index.ts +2 -0
- package/ts/models.ts +87 -1
- package/ts/rust-gitmanager-bridge.ts +53 -4
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { GitManagerError } from './classes.gitmanagererror.js';
|
|
3
|
+
import type {
|
|
4
|
+
IGitManagerArchiveInspection,
|
|
5
|
+
IGitManagerArchiveInspectionRequest,
|
|
6
|
+
} from './models.js';
|
|
7
|
+
|
|
8
|
+
/** The fixed ci-spec path-metadata ceiling of every archive. */
|
|
9
|
+
export const gitManagerArchivePathMetadataCeilingBytes = 134_217_728;
|
|
10
|
+
|
|
11
|
+
const maxInspectorOutputBytes = 64 * 1024;
|
|
12
|
+
|
|
13
|
+
type TInspectorSettlement =
|
|
14
|
+
| { kind: 'closed'; code: number | null; signal: NodeJS.Signals | null }
|
|
15
|
+
| { kind: 'spawnFailed'; error: Error };
|
|
16
|
+
|
|
17
|
+
const assertLimit = (
|
|
18
|
+
nameArg: string,
|
|
19
|
+
valueArg: number,
|
|
20
|
+
minimumArg: number,
|
|
21
|
+
maximumArg: number,
|
|
22
|
+
): void => {
|
|
23
|
+
if (!Number.isSafeInteger(valueArg) || valueArg < minimumArg || valueArg > maximumArg) {
|
|
24
|
+
throw new TypeError(
|
|
25
|
+
`${nameArg} must be a safe integer between ${minimumArg} and ${maximumArg}.`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> =>
|
|
31
|
+
typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
|
|
32
|
+
|
|
33
|
+
const isCount = (valueArg: unknown): valueArg is number =>
|
|
34
|
+
typeof valueArg === 'number' && Number.isSafeInteger(valueArg) && valueArg >= 0;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parses the inspector's single JSON result: the recomputed metadata of an
|
|
38
|
+
* accepted archive, or a thrown `GitManagerError` for a refused one.
|
|
39
|
+
*/
|
|
40
|
+
const parseInspectorResult = (stdoutArg: string): IGitManagerArchiveInspection => {
|
|
41
|
+
let parsed: unknown;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(stdoutArg);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error('Archive inspector returned invalid JSON', { cause: error });
|
|
46
|
+
}
|
|
47
|
+
if (isRecord(parsed) && parsed.status === 'rejected' && typeof parsed.message === 'string') {
|
|
48
|
+
if (parsed.reason === 'limitExceeded') {
|
|
49
|
+
throw new GitManagerError('archiveLimitExceeded', parsed.message);
|
|
50
|
+
}
|
|
51
|
+
if (parsed.reason === 'invalid') {
|
|
52
|
+
throw new GitManagerError('archiveRejected', parsed.message);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (
|
|
56
|
+
isRecord(parsed)
|
|
57
|
+
&& parsed.status === 'accepted'
|
|
58
|
+
&& isCount(parsed.sizeBytes)
|
|
59
|
+
&& typeof parsed.sha256 === 'string'
|
|
60
|
+
&& /^[0-9a-f]{64}$/.test(parsed.sha256)
|
|
61
|
+
&& isCount(parsed.extractedSizeBytes)
|
|
62
|
+
&& isCount(parsed.entryCount)
|
|
63
|
+
&& isCount(parsed.pathMetadataBytes)
|
|
64
|
+
) {
|
|
65
|
+
return {
|
|
66
|
+
sizeBytes: parsed.sizeBytes,
|
|
67
|
+
sha256: parsed.sha256,
|
|
68
|
+
extractedSizeBytes: parsed.extractedSizeBytes,
|
|
69
|
+
entryCount: parsed.entryCount,
|
|
70
|
+
pathMetadataBytes: parsed.pathMetadataBytes,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw new Error('Archive inspector returned an invalid result');
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Streams one archive into a dedicated engine inspector process and returns
|
|
78
|
+
* its recomputed metadata. The inspector reads until the archive is accepted
|
|
79
|
+
* or the first violation; on a refusal the remaining input is not read and
|
|
80
|
+
* the source is closed. Aborting `signalArg` kills the inspector and rejects
|
|
81
|
+
* with the abort reason once the process has exited.
|
|
82
|
+
*/
|
|
83
|
+
export const runArchiveInspection = async (
|
|
84
|
+
binaryPathArg: string,
|
|
85
|
+
requestArg: IGitManagerArchiveInspectionRequest,
|
|
86
|
+
signalArg: AbortSignal,
|
|
87
|
+
): Promise<IGitManagerArchiveInspection> => {
|
|
88
|
+
const maxPathMetadataBytes =
|
|
89
|
+
requestArg.maxPathMetadataBytes ?? gitManagerArchivePathMetadataCeilingBytes;
|
|
90
|
+
assertLimit('maxCompressedBytes', requestArg.maxCompressedBytes, 0, Number.MAX_SAFE_INTEGER);
|
|
91
|
+
assertLimit('maxExtractedBytes', requestArg.maxExtractedBytes, 0, Number.MAX_SAFE_INTEGER);
|
|
92
|
+
assertLimit('maxEntries', requestArg.maxEntries, 0, Number.MAX_SAFE_INTEGER);
|
|
93
|
+
assertLimit(
|
|
94
|
+
'maxPathMetadataBytes',
|
|
95
|
+
maxPathMetadataBytes,
|
|
96
|
+
1,
|
|
97
|
+
gitManagerArchivePathMetadataCeilingBytes,
|
|
98
|
+
);
|
|
99
|
+
if (signalArg.aborted) {
|
|
100
|
+
throw signalArg.reason;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const child = plugins.childProcess.spawn(binaryPathArg, [
|
|
104
|
+
'--inspect-archive-stream',
|
|
105
|
+
'--max-compressed-bytes',
|
|
106
|
+
String(requestArg.maxCompressedBytes),
|
|
107
|
+
'--max-extracted-bytes',
|
|
108
|
+
String(requestArg.maxExtractedBytes),
|
|
109
|
+
'--max-entries',
|
|
110
|
+
String(requestArg.maxEntries),
|
|
111
|
+
'--max-path-metadata-bytes',
|
|
112
|
+
String(maxPathMetadataBytes),
|
|
113
|
+
], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
114
|
+
const killInspector = (): void => {
|
|
115
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
116
|
+
child.kill('SIGKILL');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const settled = new Promise<TInspectorSettlement>((resolve) => {
|
|
121
|
+
child.once('error', (error) => {
|
|
122
|
+
killInspector();
|
|
123
|
+
resolve({ kind: 'spawnFailed', error });
|
|
124
|
+
});
|
|
125
|
+
child.once('close', (code, signal) => resolve({ kind: 'closed', code, signal }));
|
|
126
|
+
});
|
|
127
|
+
let inspectorSettled = false;
|
|
128
|
+
void settled.then(() => {
|
|
129
|
+
inspectorSettled = true;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const stdoutChunks: Buffer[] = [];
|
|
133
|
+
const stderrChunks: Buffer[] = [];
|
|
134
|
+
let outputBytes = 0;
|
|
135
|
+
let outputOverflow = false;
|
|
136
|
+
const collect = (chunksArg: Buffer[]) => (chunkArg: Buffer): void => {
|
|
137
|
+
outputBytes += chunkArg.byteLength;
|
|
138
|
+
if (outputBytes > maxInspectorOutputBytes) {
|
|
139
|
+
outputOverflow = true;
|
|
140
|
+
killInspector();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
chunksArg.push(chunkArg);
|
|
144
|
+
};
|
|
145
|
+
child.stdout.on('data', collect(stdoutChunks));
|
|
146
|
+
child.stderr.on('data', collect(stderrChunks));
|
|
147
|
+
|
|
148
|
+
// A write error is the inspector closing its input after a refusal (or
|
|
149
|
+
// dying); its exit status and result decide the outcome.
|
|
150
|
+
child.stdin.on('error', () => undefined);
|
|
151
|
+
|
|
152
|
+
let aborted = false;
|
|
153
|
+
const abortInspection = (): void => {
|
|
154
|
+
aborted = true;
|
|
155
|
+
killInspector();
|
|
156
|
+
};
|
|
157
|
+
signalArg.addEventListener('abort', abortInspection, { once: true });
|
|
158
|
+
|
|
159
|
+
let delivered = false;
|
|
160
|
+
let sourceFailure: { error: unknown } | undefined;
|
|
161
|
+
const waitForDrain = (): Promise<unknown> => Promise.race([
|
|
162
|
+
new Promise<void>((resolve) => {
|
|
163
|
+
const done = (): void => {
|
|
164
|
+
child.stdin.off('drain', done);
|
|
165
|
+
child.stdin.off('close', done);
|
|
166
|
+
resolve();
|
|
167
|
+
};
|
|
168
|
+
child.stdin.once('drain', done);
|
|
169
|
+
child.stdin.once('close', done);
|
|
170
|
+
}),
|
|
171
|
+
settled,
|
|
172
|
+
]);
|
|
173
|
+
const feed = async (): Promise<void> => {
|
|
174
|
+
const archive = requestArg.archive;
|
|
175
|
+
if (archive instanceof Uint8Array) {
|
|
176
|
+
child.stdin.end(archive);
|
|
177
|
+
delivered = true;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const iterator = archive[Symbol.asyncIterator]();
|
|
181
|
+
let exhausted = false;
|
|
182
|
+
try {
|
|
183
|
+
while (!inspectorSettled) {
|
|
184
|
+
const step = await Promise.race([
|
|
185
|
+
iterator.next().then((result) => ({ kind: 'chunk' as const, result })),
|
|
186
|
+
settled.then(() => ({ kind: 'settled' as const })),
|
|
187
|
+
]);
|
|
188
|
+
if (step.kind === 'settled' || inspectorSettled) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (step.result.done) {
|
|
192
|
+
exhausted = true;
|
|
193
|
+
child.stdin.end();
|
|
194
|
+
delivered = true;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (!(step.result.value instanceof Uint8Array)) {
|
|
198
|
+
throw new TypeError('The archive stream must yield Uint8Array chunks.');
|
|
199
|
+
}
|
|
200
|
+
if (!child.stdin.write(step.result.value)) {
|
|
201
|
+
await waitForDrain();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
sourceFailure = { error };
|
|
206
|
+
killInspector();
|
|
207
|
+
} finally {
|
|
208
|
+
if (!exhausted && !sourceFailure) {
|
|
209
|
+
// Close the source like a `for await` loop that stops early; a
|
|
210
|
+
// stalled source must not hold the inspection open.
|
|
211
|
+
void Promise.resolve(iterator.return?.()).catch(() => undefined);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const [settlement] = await Promise.all([settled, feed()]);
|
|
218
|
+
if (aborted) {
|
|
219
|
+
throw signalArg.reason;
|
|
220
|
+
}
|
|
221
|
+
if (sourceFailure) {
|
|
222
|
+
throw sourceFailure.error;
|
|
223
|
+
}
|
|
224
|
+
if (settlement.kind === 'spawnFailed') {
|
|
225
|
+
throw new Error('Failed to spawn the archive inspector', { cause: settlement.error });
|
|
226
|
+
}
|
|
227
|
+
if (outputOverflow) {
|
|
228
|
+
throw new Error('Archive inspector output exceeded its limit');
|
|
229
|
+
}
|
|
230
|
+
if (settlement.code !== 0) {
|
|
231
|
+
const stderr = Buffer.concat(stderrChunks).toString('utf8').trim();
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Archive inspector failed with ${settlement.signal ?? `status ${settlement.code}`}`
|
|
234
|
+
+ (stderr ? `: ${stderr}` : ''),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const inspection = parseInspectorResult(Buffer.concat(stdoutChunks).toString('utf8'));
|
|
238
|
+
if (!delivered) {
|
|
239
|
+
throw new Error('Archive inspector accepted an archive that was not completely delivered');
|
|
240
|
+
}
|
|
241
|
+
return inspection;
|
|
242
|
+
} finally {
|
|
243
|
+
signalArg.removeEventListener('abort', abortInspection);
|
|
244
|
+
}
|
|
245
|
+
};
|
package/ts/classes.gitmanager.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import type {
|
|
3
|
+
IGitManagerAncestryRequest,
|
|
4
|
+
IGitManagerArchiveInspection,
|
|
5
|
+
IGitManagerArchiveInspectionOptions,
|
|
6
|
+
IGitManagerArchiveInspectionRequest,
|
|
3
7
|
IGitManagerCommitDetail,
|
|
4
8
|
IGitManagerCommitRequest,
|
|
5
9
|
IGitManagerCommitSummary,
|
|
@@ -13,7 +17,6 @@ import type {
|
|
|
13
17
|
IGitManagerMergeRequest,
|
|
14
18
|
IGitManagerMergeResult,
|
|
15
19
|
IGitManagerReadFileRequest,
|
|
16
|
-
IGitManagerRef,
|
|
17
20
|
IGitManagerRefRequest,
|
|
18
21
|
IGitManagerProtectedRefsApplyResult,
|
|
19
22
|
IGitManagerSmartHttpRepositoryProtectedRefs,
|
|
@@ -37,10 +40,12 @@ import type {
|
|
|
37
40
|
IGitManagerReadinessOptions,
|
|
38
41
|
IRustGitManagerConfig,
|
|
39
42
|
TGitManagerReadinessFailureReason,
|
|
43
|
+
TGitManagerRefInventoryResult,
|
|
40
44
|
TGitManagerReadinessResult,
|
|
41
45
|
} from './models.js';
|
|
42
46
|
import { RustGitManagerBridge } from './rust-gitmanager-bridge.js';
|
|
43
47
|
import { GitManagerRepositoryService } from './classes.repositoryservice.js';
|
|
48
|
+
import { runArchiveInspection } from './classes.archiveinspection.js';
|
|
44
49
|
|
|
45
50
|
const readinessDefaultTimeoutMs = 2_000;
|
|
46
51
|
const readinessMaximumTimeoutMs = 30_000;
|
|
@@ -56,6 +61,7 @@ export class GitManager extends plugins.EventEmitter {
|
|
|
56
61
|
private stopPromise: Promise<void> | null = null;
|
|
57
62
|
private config: IRustGitManagerConfig;
|
|
58
63
|
private readonly repositoryServices = new Set<GitManagerRepositoryService>();
|
|
64
|
+
private readonly archiveInspections = new Map<AbortController, Promise<unknown>>();
|
|
59
65
|
private lifecycleEpoch = 0;
|
|
60
66
|
private smartHttpProbeUrl: string | null = null;
|
|
61
67
|
private readonly activeReadinessControllers = new Set<AbortController>();
|
|
@@ -168,12 +174,14 @@ export class GitManager extends plugins.EventEmitter {
|
|
|
168
174
|
}
|
|
169
175
|
if (!this.bridge.running) {
|
|
170
176
|
await this.closeRepositoryServices();
|
|
177
|
+
await this.abortArchiveInspections();
|
|
171
178
|
await this.bridge.terminate();
|
|
172
179
|
this.started = false;
|
|
173
180
|
return;
|
|
174
181
|
}
|
|
175
182
|
try {
|
|
176
183
|
await this.closeRepositoryServices();
|
|
184
|
+
await this.abortArchiveInspections();
|
|
177
185
|
await this.bridge.stopGitManager();
|
|
178
186
|
} finally {
|
|
179
187
|
await this.bridge.terminate();
|
|
@@ -403,7 +411,15 @@ export class GitManager extends plugins.EventEmitter {
|
|
|
403
411
|
return this.bridge.listRepositories();
|
|
404
412
|
}
|
|
405
413
|
|
|
406
|
-
|
|
414
|
+
/**
|
|
415
|
+
* Lists one page of the repository's complete ref inventory. Without
|
|
416
|
+
* `prefixes` every ref under `refs/` is selected; follow
|
|
417
|
+
* `pagination.cursor` until `exhausted`, and restart without a cursor on a
|
|
418
|
+
* `stale` result.
|
|
419
|
+
*/
|
|
420
|
+
public async listRefs(
|
|
421
|
+
requestArg: IGitManagerRefRequest,
|
|
422
|
+
): Promise<TGitManagerRefInventoryResult> {
|
|
407
423
|
this.assertRunning();
|
|
408
424
|
return this.bridge.listRefs(requestArg);
|
|
409
425
|
}
|
|
@@ -432,6 +448,51 @@ export class GitManager extends plugins.EventEmitter {
|
|
|
432
448
|
return this.bridge.readFile(requestArg);
|
|
433
449
|
}
|
|
434
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Answers `git merge-base --is-ancestor` exactly within a work budget.
|
|
453
|
+
* Anything short of an exact answer throws a `GitManagerError`
|
|
454
|
+
* (`invalidObjectId`, `objectNotFound`, `objectNotCommit`,
|
|
455
|
+
* `ancestryBudgetExceeded`); it never degrades to `false`.
|
|
456
|
+
*/
|
|
457
|
+
public async isAncestor(requestArg: IGitManagerAncestryRequest): Promise<boolean> {
|
|
458
|
+
this.assertRunning();
|
|
459
|
+
return this.bridge.isAncestor(requestArg);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Inspects a receiver-side `tar.gz` archive in a dedicated engine process:
|
|
464
|
+
* recomputes its compressed size, SHA-256, extracted bytes, entry count,
|
|
465
|
+
* and path metadata bytes while enforcing the limits and the ci-spec path
|
|
466
|
+
* and link rules. A refused archive throws a `GitManagerError` with code
|
|
467
|
+
* `archiveLimitExceeded` or `archiveRejected`; an invalid limit throws a
|
|
468
|
+
* `TypeError`. `stop()` aborts inspections in flight.
|
|
469
|
+
*/
|
|
470
|
+
public async inspectArchive(
|
|
471
|
+
requestArg: IGitManagerArchiveInspectionRequest,
|
|
472
|
+
optionsArg: IGitManagerArchiveInspectionOptions = {},
|
|
473
|
+
): Promise<IGitManagerArchiveInspection> {
|
|
474
|
+
this.assertRunning();
|
|
475
|
+
const controller = new AbortController();
|
|
476
|
+
const callerSignal = optionsArg.signal;
|
|
477
|
+
const abortForCaller = (): void => {
|
|
478
|
+
controller.abort(callerSignal?.reason);
|
|
479
|
+
};
|
|
480
|
+
if (callerSignal?.aborted) {
|
|
481
|
+
throw callerSignal.reason;
|
|
482
|
+
}
|
|
483
|
+
callerSignal?.addEventListener('abort', abortForCaller, { once: true });
|
|
484
|
+
const inspection = this.bridge.findBinary().then((binaryPath) => {
|
|
485
|
+
return runArchiveInspection(binaryPath, requestArg, controller.signal);
|
|
486
|
+
});
|
|
487
|
+
this.archiveInspections.set(controller, inspection);
|
|
488
|
+
try {
|
|
489
|
+
return await inspection;
|
|
490
|
+
} finally {
|
|
491
|
+
callerSignal?.removeEventListener('abort', abortForCaller);
|
|
492
|
+
this.archiveInspections.delete(controller);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
435
496
|
public async listCommits(requestArg: IGitManagerListCommitsRequest): Promise<IGitManagerCommitSummary[]> {
|
|
436
497
|
this.assertRunning();
|
|
437
498
|
return this.bridge.listCommits(requestArg);
|
|
@@ -667,6 +728,15 @@ export class GitManager extends plugins.EventEmitter {
|
|
|
667
728
|
}
|
|
668
729
|
}
|
|
669
730
|
|
|
731
|
+
private async abortArchiveInspections(): Promise<void> {
|
|
732
|
+
const inspections = [...this.archiveInspections];
|
|
733
|
+
this.archiveInspections.clear();
|
|
734
|
+
for (const [controller] of inspections) {
|
|
735
|
+
controller.abort(new Error('GitManager stopped during archive inspection'));
|
|
736
|
+
}
|
|
737
|
+
await Promise.all(inspections.map(([, inspection]) => inspection.catch(() => undefined)));
|
|
738
|
+
}
|
|
739
|
+
|
|
670
740
|
private async closeRepositoryServices(): Promise<void> {
|
|
671
741
|
const services = [...this.repositoryServices];
|
|
672
742
|
this.repositoryServices.clear();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TGitManagerErrorCode } from './models.js';
|
|
2
|
+
|
|
3
|
+
const gitManagerErrorCodes: ReadonlySet<string> = new Set<TGitManagerErrorCode>([
|
|
4
|
+
'invalidObjectId',
|
|
5
|
+
'objectNotFound',
|
|
6
|
+
'objectNotCommit',
|
|
7
|
+
'ancestryBudgetExceeded',
|
|
8
|
+
'archiveLimitExceeded',
|
|
9
|
+
'archiveRejected',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
export const isGitManagerErrorCode = (valueArg: unknown): valueArg is TGitManagerErrorCode =>
|
|
13
|
+
typeof valueArg === 'string' && gitManagerErrorCodes.has(valueArg);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A failure callers can branch on through its stable `code`. Other failures,
|
|
17
|
+
* such as an unknown repository or an engine that is not running, remain
|
|
18
|
+
* plain errors.
|
|
19
|
+
*/
|
|
20
|
+
export class GitManagerError extends Error {
|
|
21
|
+
public readonly code: TGitManagerErrorCode;
|
|
22
|
+
|
|
23
|
+
constructor(codeArg: TGitManagerErrorCode, messageArg: string, optionsArg?: ErrorOptions) {
|
|
24
|
+
super(messageArg, optionsArg);
|
|
25
|
+
this.name = 'GitManagerError';
|
|
26
|
+
this.code = codeArg;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
IGitManagerRepositoryServiceRequest,
|
|
4
4
|
IGitManagerRepositoryServiceResult,
|
|
5
5
|
IRustGitManagerRepositoryServiceDescriptor,
|
|
6
|
+
TGitManagerRepositoryServiceFailure,
|
|
6
7
|
} from './models.js';
|
|
7
8
|
|
|
8
9
|
class ByteLimitTransform extends plugins.stream.Transform {
|
|
@@ -11,7 +12,7 @@ class ByteLimitTransform extends plugins.stream.Transform {
|
|
|
11
12
|
constructor(
|
|
12
13
|
private readonly maximumBytes: number,
|
|
13
14
|
private readonly label: string,
|
|
14
|
-
private readonly onLimit: (
|
|
15
|
+
private readonly onLimit: () => void,
|
|
15
16
|
) {
|
|
16
17
|
super();
|
|
17
18
|
}
|
|
@@ -28,9 +29,8 @@ class ByteLimitTransform extends plugins.stream.Transform {
|
|
|
28
29
|
: Buffer.from(chunkArg);
|
|
29
30
|
this.bytes += chunk.byteLength;
|
|
30
31
|
if (!Number.isSafeInteger(this.bytes) || this.bytes > this.maximumBytes) {
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
callbackArg(error);
|
|
32
|
+
this.onLimit();
|
|
33
|
+
callbackArg(new Error(this.label + ' exceeded the configured size limit'));
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
callbackArg(null, chunk);
|
|
@@ -82,6 +82,7 @@ export class GitManagerRepositoryService {
|
|
|
82
82
|
public readonly completed: Promise<IGitManagerRepositoryServiceResult>;
|
|
83
83
|
private readonly child: plugins.childProcess.ChildProcessWithoutNullStreams;
|
|
84
84
|
private completionSettled = false;
|
|
85
|
+
private failure: TGitManagerRepositoryServiceFailure | null = null;
|
|
85
86
|
private timeout?: NodeJS.Timeout;
|
|
86
87
|
|
|
87
88
|
constructor(
|
|
@@ -134,25 +135,31 @@ export class GitManagerRepositoryService {
|
|
|
134
135
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
135
136
|
windowsHide: true,
|
|
136
137
|
});
|
|
137
|
-
const
|
|
138
|
-
this.
|
|
139
|
-
return errorArg;
|
|
140
|
-
};
|
|
138
|
+
const failWith = (failureArg: TGitManagerRepositoryServiceFailure) =>
|
|
139
|
+
() => this.fail(failureArg);
|
|
141
140
|
const input = new ByteLimitTransform(
|
|
142
141
|
descriptorArg.maxRequestBytes,
|
|
143
142
|
'Git repository service request',
|
|
144
|
-
|
|
143
|
+
failWith('requestLimitExceeded'),
|
|
145
144
|
);
|
|
146
145
|
const output = new ByteLimitTransform(
|
|
147
146
|
descriptorArg.maxResponseBytes,
|
|
148
147
|
'Git repository service response',
|
|
149
|
-
|
|
148
|
+
failWith('responseLimitExceeded'),
|
|
150
149
|
);
|
|
151
150
|
const stderr = new ByteLimitTransform(
|
|
152
151
|
128 * 1024,
|
|
153
152
|
'Git repository service stderr',
|
|
154
|
-
|
|
153
|
+
failWith('stderrLimitExceeded'),
|
|
155
154
|
);
|
|
155
|
+
// The service owns every stream error: a consumer's pipe() into stdin
|
|
156
|
+
// would otherwise rethrow an unhandled error. Failures surface through
|
|
157
|
+
// `completed`. Git closing its stdin early is reported by its exit
|
|
158
|
+
// status, so write errors on the child's stdin end only the pipe.
|
|
159
|
+
for (const ownedStream of [input, output, stderr, this.child.stdout, this.child.stderr]) {
|
|
160
|
+
ownedStream.on('error', failWith('streamFailed'));
|
|
161
|
+
}
|
|
162
|
+
this.child.stdin.on('error', () => undefined);
|
|
156
163
|
input.pipe(this.child.stdin);
|
|
157
164
|
this.child.stdout.pipe(output);
|
|
158
165
|
this.child.stderr.pipe(stderr);
|
|
@@ -173,11 +180,12 @@ export class GitManagerRepositoryService {
|
|
|
173
180
|
resolve({
|
|
174
181
|
exitCode,
|
|
175
182
|
signal: signal as NodeJS.Signals | null,
|
|
183
|
+
failure: this.failure,
|
|
176
184
|
});
|
|
177
185
|
});
|
|
178
186
|
});
|
|
179
187
|
this.timeout = setTimeout(() => {
|
|
180
|
-
this.
|
|
188
|
+
this.fail('processTimeout');
|
|
181
189
|
}, descriptorArg.processTimeoutMs);
|
|
182
190
|
this.timeout.unref();
|
|
183
191
|
}
|
|
@@ -198,12 +206,33 @@ export class GitManagerRepositoryService {
|
|
|
198
206
|
this.child.kill('SIGKILL');
|
|
199
207
|
}
|
|
200
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Kills the Git process tree and releases every stream, including output
|
|
211
|
+
* the caller never read, so `completed` settles without depending on the
|
|
212
|
+
* caller draining it.
|
|
213
|
+
*/
|
|
201
214
|
public async close(): Promise<void> {
|
|
202
215
|
this.stdin.destroy();
|
|
203
216
|
this.terminate();
|
|
217
|
+
for (const ownedStream of [
|
|
218
|
+
this.stdout,
|
|
219
|
+
this.stderr,
|
|
220
|
+
this.child.stdout,
|
|
221
|
+
this.child.stderr,
|
|
222
|
+
]) {
|
|
223
|
+
ownedStream.destroy();
|
|
224
|
+
}
|
|
204
225
|
await this.completed.catch(() => undefined);
|
|
205
226
|
}
|
|
206
227
|
|
|
228
|
+
/** Records the first failure and terminates the Git process tree. */
|
|
229
|
+
private fail(failureArg: TGitManagerRepositoryServiceFailure): void {
|
|
230
|
+
if (!this.failure && !this.completionSettled) {
|
|
231
|
+
this.failure = failureArg;
|
|
232
|
+
}
|
|
233
|
+
this.terminate();
|
|
234
|
+
}
|
|
235
|
+
|
|
207
236
|
private clearTimeout(): void {
|
|
208
237
|
if (this.timeout) {
|
|
209
238
|
clearTimeout(this.timeout);
|
package/ts/index.ts
CHANGED
|
@@ -2,3 +2,5 @@ export * from './models.js';
|
|
|
2
2
|
export * from './rust-gitmanager-bridge.js';
|
|
3
3
|
export * from './classes.gitmanager.js';
|
|
4
4
|
export * from './classes.repositoryservice.js';
|
|
5
|
+
export * from './classes.gitmanagererror.js';
|
|
6
|
+
export { gitManagerArchivePathMetadataCeilingBytes } from './classes.archiveinspection.js';
|
package/ts/models.ts
CHANGED
|
@@ -89,6 +89,71 @@ export interface IGitManagerRepositoryArchive
|
|
|
89
89
|
entryCount: number;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Asks whether `ancestorObjectId` is reachable from `descendantObjectId`
|
|
94
|
+
* (`git merge-base --is-ancestor`; a commit is its own ancestor). Both must be
|
|
95
|
+
* full lowercase object IDs of commits in the repository; revision
|
|
96
|
+
* expressions, abbreviations, and tag objects are refused.
|
|
97
|
+
*/
|
|
98
|
+
export interface IGitManagerAncestryRequest extends IGitManagerRepositorySelector {
|
|
99
|
+
ancestorObjectId: string;
|
|
100
|
+
descendantObjectId: string;
|
|
101
|
+
/**
|
|
102
|
+
* Wall-clock work budget for the object lookup and the history walk,
|
|
103
|
+
* 1 to 60000 ms, default 10000. Exceeding it throws a `GitManagerError`
|
|
104
|
+
* with code `ancestryBudgetExceeded`.
|
|
105
|
+
*/
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Limits for inspecting one `tar.gz` archive. Every limit is inclusive.
|
|
111
|
+
*/
|
|
112
|
+
export interface IGitManagerArchiveInspectionRequest {
|
|
113
|
+
/** The archive bytes, or a stream of them; it is consumed and closed. */
|
|
114
|
+
archive: Uint8Array | AsyncIterable<Uint8Array>;
|
|
115
|
+
maxCompressedBytes: number;
|
|
116
|
+
maxExtractedBytes: number;
|
|
117
|
+
maxEntries: number;
|
|
118
|
+
/**
|
|
119
|
+
* Ceiling for the UTF-8 bytes of every effective entry path and symlink
|
|
120
|
+
* target, 1 to 134217728 (the fixed ci-spec ceiling, and the default).
|
|
121
|
+
*/
|
|
122
|
+
maxPathMetadataBytes?: number;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface IGitManagerArchiveInspectionOptions {
|
|
126
|
+
signal?: AbortSignal;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Metadata recomputed from an accepted archive; nothing is taken on trust. */
|
|
130
|
+
export interface IGitManagerArchiveInspection {
|
|
131
|
+
sizeBytes: number;
|
|
132
|
+
sha256: string;
|
|
133
|
+
extractedSizeBytes: number;
|
|
134
|
+
entryCount: number;
|
|
135
|
+
pathMetadataBytes: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Stable codes of `GitManagerError`.
|
|
140
|
+
*
|
|
141
|
+
* - `invalidObjectId`: an object ID is not a full lowercase SHA-1 or SHA-256.
|
|
142
|
+
* - `objectNotFound`: the object does not exist in the repository.
|
|
143
|
+
* - `objectNotCommit`: the object exists but is not a commit.
|
|
144
|
+
* - `ancestryBudgetExceeded`: the ancestry check exceeded `timeoutMs`; the
|
|
145
|
+
* answer is unknown, never `false`.
|
|
146
|
+
* - `archiveLimitExceeded`: the archive exceeds a requested limit.
|
|
147
|
+
* - `archiveRejected`: the archive breaks a format, path, or link rule.
|
|
148
|
+
*/
|
|
149
|
+
export type TGitManagerErrorCode =
|
|
150
|
+
| 'invalidObjectId'
|
|
151
|
+
| 'objectNotFound'
|
|
152
|
+
| 'objectNotCommit'
|
|
153
|
+
| 'ancestryBudgetExceeded'
|
|
154
|
+
| 'archiveLimitExceeded'
|
|
155
|
+
| 'archiveRejected';
|
|
156
|
+
|
|
92
157
|
export interface IGitManagerRepositoryLfsPointer {
|
|
93
158
|
oid: string;
|
|
94
159
|
sizeBytes: number;
|
|
@@ -115,9 +180,26 @@ export interface IRustGitManagerRepositoryServiceDescriptor
|
|
|
115
180
|
idleTimeoutSeconds: number;
|
|
116
181
|
}
|
|
117
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Why GitManager terminated a repository service process. The value says the
|
|
185
|
+
* transfer was cut, not that the repository is unchanged: an SSH-style
|
|
186
|
+
* receive-pack has no receive gate, so Git updates refs as soon as it has read
|
|
187
|
+
* a complete pack, and excess bytes arriving after that cannot undo those
|
|
188
|
+
* updates. Reconcile refs from the repository after every receive-pack,
|
|
189
|
+
* whatever `failure` says.
|
|
190
|
+
*/
|
|
191
|
+
export type TGitManagerRepositoryServiceFailure =
|
|
192
|
+
| 'requestLimitExceeded'
|
|
193
|
+
| 'responseLimitExceeded'
|
|
194
|
+
| 'stderrLimitExceeded'
|
|
195
|
+
| 'streamFailed'
|
|
196
|
+
| 'processTimeout';
|
|
197
|
+
|
|
118
198
|
export interface IGitManagerRepositoryServiceResult {
|
|
119
199
|
exitCode: number | null;
|
|
120
200
|
signal: NodeJS.Signals | null;
|
|
201
|
+
/** The first failure that made GitManager terminate the process; null otherwise. */
|
|
202
|
+
failure: TGitManagerRepositoryServiceFailure | null;
|
|
121
203
|
}
|
|
122
204
|
|
|
123
205
|
export interface IGitManagerRestoreBundleRequest
|
|
@@ -231,6 +313,9 @@ export type {
|
|
|
231
313
|
IGitManagerMergeResult,
|
|
232
314
|
IGitManagerReadFileRequest,
|
|
233
315
|
IGitManagerRef,
|
|
316
|
+
IGitManagerRefHead,
|
|
317
|
+
IGitManagerRefInventoryPage,
|
|
318
|
+
IGitManagerRefInventoryStale,
|
|
234
319
|
IGitManagerRefRequest,
|
|
235
320
|
IGitManagerRepositoryInfo,
|
|
236
321
|
IGitManagerRepositorySelector,
|
|
@@ -243,6 +328,7 @@ export type {
|
|
|
243
328
|
TGitManagerMergeMethod,
|
|
244
329
|
TGitManagerMergeStatus,
|
|
245
330
|
TGitManagerObjectType,
|
|
246
|
-
|
|
331
|
+
TGitManagerRefInventoryPagination,
|
|
332
|
+
TGitManagerRefInventoryResult,
|
|
247
333
|
TGitManagerTreeEntryType,
|
|
248
334
|
} from '@foss.global/interfaces';
|