@git.zone/tspack 1.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 +26 -0
- package/cli.js +3 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/archive.d.ts +3 -0
- package/dist_ts/archive.js +60 -0
- package/dist_ts/classes.tspack.d.ts +11 -0
- package/dist_ts/classes.tspack.js +181 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/interfaces.d.ts +60 -0
- package/dist_ts/interfaces.js +2 -0
- package/dist_ts/plugins.d.ts +13 -0
- package/dist_ts/plugins.js +16 -0
- package/dist_ts/tspack.cli.d.ts +1 -0
- package/dist_ts/tspack.cli.js +53 -0
- package/dist_ts/validation.d.ts +32 -0
- package/dist_ts/validation.js +132 -0
- package/license.md +21 -0
- package/package.json +49 -0
- package/readme.md +181 -0
- package/readme.plan.md +26 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/archive.ts +60 -0
- package/ts/classes.tspack.ts +170 -0
- package/ts/index.ts +3 -0
- package/ts/interfaces.ts +68 -0
- package/ts/plugins.ts +16 -0
- package/ts/tspack.cli.ts +44 -0
- package/ts/validation.ts +136 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export const limits = { file: 2 * 1024 ** 3, total: 8 * 1024 ** 3, files: 10_000, json: 16 * 1024 ** 2 };
|
|
3
|
+
export class TsPackError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = 'TsPackError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function requireValue(value, code, message) {
|
|
12
|
+
if (!value)
|
|
13
|
+
throw new TsPackError(code, message);
|
|
14
|
+
}
|
|
15
|
+
export const sha256 = (value) => plugins.crypto.createHash('sha256').update(value).digest('hex');
|
|
16
|
+
export const isDigest = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
17
|
+
export const isCommit = (value) => typeof value === 'string' && /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(value);
|
|
18
|
+
export const isName = (value) => typeof value === 'string' && /^[a-z0-9][a-z0-9._-]{0,99}$/.test(value);
|
|
19
|
+
export const isVersion = (value) => typeof value === 'string' && value.length <= 64 && /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/.test(value);
|
|
20
|
+
export const isPath = (value) => typeof value === 'string' && value.length <= 240 &&
|
|
21
|
+
/^[a-zA-Z0-9@_+./-]+$/.test(value) && !value.startsWith('/') && value.split('/').every((part) => part && part !== '.' && part !== '..');
|
|
22
|
+
export const jsonBytes = (value) => Buffer.from(JSON.stringify(value, null, 2) + '\n');
|
|
23
|
+
export function object(value, allowed) {
|
|
24
|
+
requireValue(value && typeof value === 'object' && !Array.isArray(value) && !plugins.util.types.isProxy(value) &&
|
|
25
|
+
[Object.prototype, null].includes(Object.getPrototypeOf(value)), 'invalid_config', 'Expected a plain record.');
|
|
26
|
+
requireValue(Reflect.ownKeys(value).every((key) => typeof key === 'string' &&
|
|
27
|
+
(!allowed || allowed.includes(key)) && Object.getOwnPropertyDescriptor(value, key)?.enumerable &&
|
|
28
|
+
'value' in Object.getOwnPropertyDescriptor(value, key)), 'invalid_config', 'Unexpected record property.');
|
|
29
|
+
}
|
|
30
|
+
export function normalizeConfig(value, packageName) {
|
|
31
|
+
object(value, ['schemaVersion', 'name', 'outputDirectory', 'bundles']);
|
|
32
|
+
const name = value.name ?? packageName.replace(/^@/, '').replace('/', '-');
|
|
33
|
+
const outputDirectory = value.outputDirectory ?? 'dist_pack';
|
|
34
|
+
requireValue(value.schemaVersion === 1 && isName(name) && isPath(outputDirectory) &&
|
|
35
|
+
Array.isArray(value.bundles) && value.bundles.length > 0 && value.bundles.length <= 128, 'invalid_config', 'Invalid tspack configuration.');
|
|
36
|
+
let count = 0;
|
|
37
|
+
const bundles = value.bundles.map((bundle) => {
|
|
38
|
+
object(bundle, ['id', 'files']);
|
|
39
|
+
requireValue(isName(bundle.id) && Array.isArray(bundle.files) && bundle.files.length > 0, 'invalid_config', 'A bundle requires an identifier and explicit files.');
|
|
40
|
+
const files = bundle.files.map((file) => {
|
|
41
|
+
object(file, ['source', 'path', 'executable', 'sha256']);
|
|
42
|
+
requireValue(isPath(file.source) && isPath(file.path) && file.path !== 'tspack.json' && !file.path.startsWith('tspack.json/') &&
|
|
43
|
+
(!Object.hasOwn(file, 'executable') || typeof file.executable === 'boolean') &&
|
|
44
|
+
(!Object.hasOwn(file, 'sha256') || isDigest(file.sha256)), 'invalid_config', 'Invalid bundle file.');
|
|
45
|
+
return { source: file.source, path: file.path, executable: file.executable === true,
|
|
46
|
+
...(file.sha256 ? { sha256: file.sha256 } : {}) };
|
|
47
|
+
}).sort((a, b) => a.path.localeCompare(b.path, 'en'));
|
|
48
|
+
count += files.length;
|
|
49
|
+
requireValue(new Set(files.map((file) => file.path)).size === files.length &&
|
|
50
|
+
files.every((file) => !files.some((other) => other.path.startsWith(file.path + '/'))), 'invalid_config', 'Bundle paths collide.');
|
|
51
|
+
return { id: bundle.id, files };
|
|
52
|
+
}).sort((a, b) => a.id.localeCompare(b.id, 'en'));
|
|
53
|
+
requireValue(count <= limits.files && new Set(bundles.map((bundle) => bundle.id)).size === bundles.length, 'invalid_config', 'Too many files or duplicate bundle identifiers.');
|
|
54
|
+
return { schemaVersion: 1, name, outputDirectory, bundles };
|
|
55
|
+
}
|
|
56
|
+
export function validateFileRecord(value) {
|
|
57
|
+
object(value, ['sha256', 'size', 'mode']);
|
|
58
|
+
requireValue(isDigest(value.sha256) && typeof value.size === 'number' && Number.isSafeInteger(value.size) &&
|
|
59
|
+
value.size >= 0 && value.size <= limits.file && (value.mode === 0o644 || value.mode === 0o755), 'invalid_manifest', 'Invalid file identity.');
|
|
60
|
+
}
|
|
61
|
+
export function validateManifest(value) {
|
|
62
|
+
object(value, ['format', 'packageName', 'name', 'version', 'sourceCommit', 'sourceDirty', 'publishable', 'configurationSha256', 'artifacts']);
|
|
63
|
+
requireValue(value.format === 'tspack.release.v1' && typeof value.packageName === 'string' && value.packageName.length <= 214 &&
|
|
64
|
+
isName(value.name) && isVersion(value.version) && isCommit(value.sourceCommit) &&
|
|
65
|
+
typeof value.sourceDirty === 'boolean' && typeof value.publishable === 'boolean' &&
|
|
66
|
+
(!value.publishable || !value.sourceDirty) && isDigest(value.configurationSha256) &&
|
|
67
|
+
Array.isArray(value.artifacts) && value.artifacts.length > 0 && value.artifacts.length <= 128, 'invalid_manifest', 'Invalid artifact-set manifest.');
|
|
68
|
+
const ids = new Set();
|
|
69
|
+
const names = new Set();
|
|
70
|
+
let total = 0;
|
|
71
|
+
let count = 0;
|
|
72
|
+
for (const artifact of value.artifacts) {
|
|
73
|
+
object(artifact, ['id', 'name', 'sha256', 'size', 'files']);
|
|
74
|
+
requireValue(isName(artifact.id) && !ids.has(artifact.id) &&
|
|
75
|
+
isPath(artifact.name) && artifact.name === `${value.name}-${value.version}-${artifact.id}.tar.gz` && !names.has(artifact.name) &&
|
|
76
|
+
isDigest(artifact.sha256) && typeof artifact.size === 'number' && Number.isSafeInteger(artifact.size) &&
|
|
77
|
+
artifact.size > 0 && artifact.size <= limits.total, 'invalid_manifest', 'Invalid archive identity.');
|
|
78
|
+
ids.add(artifact.id);
|
|
79
|
+
names.add(artifact.name);
|
|
80
|
+
object(artifact.files);
|
|
81
|
+
requireValue(Object.hasOwn(artifact.files, 'tspack.json') && Object.keys(artifact.files).length > 1, 'invalid_manifest', 'Bundle metadata or selected files are missing.');
|
|
82
|
+
for (const [name, record] of Object.entries(artifact.files)) {
|
|
83
|
+
requireValue(isPath(name), 'invalid_manifest', 'Invalid archive member path.');
|
|
84
|
+
validateFileRecord(record);
|
|
85
|
+
total += record.size;
|
|
86
|
+
count++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
requireValue(total <= limits.total && count <= limits.files + 128, 'invalid_manifest', 'Artifact set exceeds its limits.');
|
|
90
|
+
}
|
|
91
|
+
export async function noSymlinks(root, relative, allowMissing = false) {
|
|
92
|
+
requireValue(isPath(relative), 'unsafe_input', 'A canonical project-relative path is required.');
|
|
93
|
+
let current = root;
|
|
94
|
+
for (const part of relative.split('/')) {
|
|
95
|
+
current = plugins.path.join(current, part);
|
|
96
|
+
try {
|
|
97
|
+
requireValue(!(await plugins.fs.lstat(current)).isSymbolicLink(), 'unsafe_input', 'Symlink paths are not package inputs or outputs.');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (allowMissing && error.code === 'ENOENT')
|
|
101
|
+
break;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return plugins.path.join(root, relative);
|
|
106
|
+
}
|
|
107
|
+
export async function readJson(file) {
|
|
108
|
+
const stat = await plugins.fs.lstat(file);
|
|
109
|
+
requireValue(stat.isFile() && stat.size <= limits.json, 'invalid_manifest', 'Expected bounded regular JSON metadata.');
|
|
110
|
+
const bytes = await plugins.fs.readFile(file);
|
|
111
|
+
requireValue(bytes.length <= limits.json, 'invalid_manifest', 'JSON metadata exceeds its limit.');
|
|
112
|
+
try {
|
|
113
|
+
return { value: JSON.parse(bytes.toString('utf8')), bytes };
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new TsPackError('invalid_manifest', 'Invalid JSON metadata.');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export async function digestFile(file) {
|
|
120
|
+
const stat = await plugins.fs.lstat(file);
|
|
121
|
+
requireValue(stat.isFile() && stat.size <= limits.total, 'integrity', 'Expected a bounded regular artifact file.');
|
|
122
|
+
const hash = plugins.crypto.createHash('sha256');
|
|
123
|
+
let size = 0;
|
|
124
|
+
for await (const chunk of plugins.nodeFs.createReadStream(file)) {
|
|
125
|
+
size += chunk.length;
|
|
126
|
+
requireValue(size <= stat.size, 'integrity', 'Artifact changed while reading.');
|
|
127
|
+
hash.update(chunk);
|
|
128
|
+
}
|
|
129
|
+
requireValue(size === stat.size, 'integrity', 'Artifact size changed while reading.');
|
|
130
|
+
return { sha256: hash.digest('hex'), size };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3ZhbGlkYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFHeEMsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFFLEdBQUcsSUFBSSxJQUFJLENBQUMsRUFBRSxDQUFDO0FBQ3pHLE1BQU0sT0FBTyxXQUFZLFNBQVEsS0FBSztJQUNSO0lBQTVCLFlBQTRCLElBQVksRUFBRSxPQUFlO1FBQ3ZELEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQURXLFNBQUksR0FBSixJQUFJLENBQVE7UUFFdEMsSUFBSSxDQUFDLElBQUksR0FBRyxhQUFhLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBQ0QsTUFBTSxVQUFVLFlBQVksQ0FBQyxLQUFjLEVBQUUsSUFBWSxFQUFFLE9BQWU7SUFDeEUsSUFBSSxDQUFDLEtBQUs7UUFBRSxNQUFNLElBQUksV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBQ0QsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBc0IsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNsSCxNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxLQUFjLEVBQW1CLEVBQUUsQ0FBQyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FB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
|
package/license.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Task Venture Capital GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@git.zone/tspack",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Verified executable and asset bundles for GitZone projects.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist_ts/index.js",
|
|
7
|
+
"typings": "dist_ts/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist_ts/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"tspack": "./cli.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@push.rocks/smartarchive": "5.3.0",
|
|
16
|
+
"@push.rocks/smartconfig": "6.2.2"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@git.zone/tsbuild": "4.4.2",
|
|
20
|
+
"@git.zone/tsrun": "2.0.6",
|
|
21
|
+
"@git.zone/tstest": "4.0.0",
|
|
22
|
+
"@types/node": "26.4.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"ts/**/*",
|
|
26
|
+
"dist_ts/**/*",
|
|
27
|
+
"cli.js",
|
|
28
|
+
".smartconfig.json",
|
|
29
|
+
"readme.md",
|
|
30
|
+
"license.md"
|
|
31
|
+
],
|
|
32
|
+
"author": "Task Venture Capital GmbH",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://code.foss.global/git.zone/tspack.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://code.foss.global/git.zone/tspack",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://community.foss.global/"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsbuild tsfolders",
|
|
47
|
+
"test": "tstest test/ --verbose --logfile --timeout 60"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @git.zone/tspack
|
|
2
|
+
|
|
3
|
+
TsPack packages compiled executables and supporting assets into verified tar.gz
|
|
4
|
+
bundles. It records each file's digest and executable role, seals the complete
|
|
5
|
+
artifact set, and verifies archive contents without extracting filesystem paths.
|
|
6
|
+
TsDeno and TsRust own compilation; GitZone and CI own publication.
|
|
7
|
+
|
|
8
|
+
## Issue Reporting and Security
|
|
9
|
+
|
|
10
|
+
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
11
|
+
|
|
12
|
+
## Install and configure
|
|
13
|
+
|
|
14
|
+
Use Node.js 22 or newer in a Git project with a committed `package.json`:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm install --save-dev @git.zone/tspack
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Add the explicit bundle contents to `.smartconfig.json`. Input files must already
|
|
21
|
+
exist. Add `dist_pack/` to the project's `.gitignore`.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"@git.zone/tspack": {
|
|
26
|
+
"schemaVersion": 1,
|
|
27
|
+
"name": "example-control",
|
|
28
|
+
"bundles": [
|
|
29
|
+
{
|
|
30
|
+
"id": "linux-amd64",
|
|
31
|
+
"files": [
|
|
32
|
+
{ "source": "dist_binary/control", "path": "control", "executable": true },
|
|
33
|
+
{ "source": "dist_binary/engine", "path": "engine", "executable": true },
|
|
34
|
+
{ "source": "license.md", "path": "license.md" },
|
|
35
|
+
{ "source": "notices/runtime.txt", "path": "third-party-notices.txt" }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`name` defaults to the package name with its scope separator replaced by a hyphen.
|
|
44
|
+
`outputDirectory` defaults to `dist_pack`. Both are project-relative configuration;
|
|
45
|
+
input and archive paths must be canonical relative paths. A file may specify
|
|
46
|
+
`sha256` to require an upstream digest. Files are mode 0644 unless `executable`
|
|
47
|
+
is true, which records mode 0755. Inputs and their ancestors cannot be symlinks.
|
|
48
|
+
|
|
49
|
+
There is no implicit directory sweep. Select notices, provenance and source
|
|
50
|
+
materials explicitly according to the component's distribution requirements.
|
|
51
|
+
TsPack does not automatically publish application source, vendor dependencies,
|
|
52
|
+
rebuild runtimes or reject accepted licenses such as LGPL.
|
|
53
|
+
|
|
54
|
+
## Package and verify
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
# Development artifacts are explicitly marked unpublishable.
|
|
58
|
+
pnpm exec tspack pack
|
|
59
|
+
|
|
60
|
+
# Release builds require clean Git source at v<package.json version>.
|
|
61
|
+
pnpm exec tspack pack --release
|
|
62
|
+
|
|
63
|
+
# An alternate JSON file contains the tspack configuration block directly.
|
|
64
|
+
pnpm exec tspack pack --release --config dist_binary/pack-inputs.json
|
|
65
|
+
|
|
66
|
+
# Verify the complete retained artifact set against a trusted manifest digest.
|
|
67
|
+
pnpm exec tspack verify dist_pack/example-control-1.2.3-COMMITPREFIX \
|
|
68
|
+
--release --sha256 MANIFEST_SHA256
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Commands return one JSON result with the output directory, manifest digest,
|
|
72
|
+
publishable state, reuse state and bundle count. Invalid arguments or failed
|
|
73
|
+
verification return a nonzero exit code.
|
|
74
|
+
|
|
75
|
+
Each artifact directory contains:
|
|
76
|
+
|
|
77
|
+
- One `NAME-VERSION-BUNDLE.tar.gz` per configured bundle.
|
|
78
|
+
- `tspack-manifest.json`: package, source, configuration, archive and file identities.
|
|
79
|
+
- `SHA256SUMS.txt`: digests of the manifest and every archive.
|
|
80
|
+
|
|
81
|
+
Each bundle includes its selected files plus `tspack.json`, which binds the same
|
|
82
|
+
source/configuration identity and selected file records. `tspack.json` is reserved.
|
|
83
|
+
Verification rejects extra or missing assets, duplicate or undeclared archive
|
|
84
|
+
members, links, unexpected modes, changed metadata and digest mismatches. Archive
|
|
85
|
+
verification never writes member paths to disk.
|
|
86
|
+
|
|
87
|
+
Files are copied into private staging while their bytes and source file identity
|
|
88
|
+
are checked. Archives use sorted members, fixed timestamps and ownership, explicit
|
|
89
|
+
modes and streaming compression. The output directory is published only after the
|
|
90
|
+
whole set verifies. Identical inputs produce identical bytes with the same
|
|
91
|
+
packaging/compression toolchain; this does not claim the compilers themselves are
|
|
92
|
+
reproducible. Failed packaging removes only its own temporary staging directory.
|
|
93
|
+
|
|
94
|
+
Limits are 2 GiB per selected file, 8 GiB total uncompressed content, 10,000 selected
|
|
95
|
+
files, 128 bundles, and 16 MiB for JSON metadata. Paths are portable ASCII relative
|
|
96
|
+
names up to 240 characters. The source checkout, selected build tools and local
|
|
97
|
+
filesystem are trusted; these checks do not sandbox malicious project code or a
|
|
98
|
+
concurrent privileged writer. A checksum establishes content identity. Obtain the
|
|
99
|
+
expected manifest digest through a trusted release channel to authenticate it.
|
|
100
|
+
|
|
101
|
+
## Retain release bytes for retries
|
|
102
|
+
|
|
103
|
+
Store the entire sealed artifact directory using the CI provider's artifact
|
|
104
|
+
storage **before** publishing any release asset. A retry must restore that exact
|
|
105
|
+
directory and verify it before deciding whether compilation is necessary:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
pnpm exec tspack pack --release --reuse
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If a retained set exists, this command verifies its complete contents and requires
|
|
112
|
+
the same clean tagged source, package/version and configuration. It returns the
|
|
113
|
+
original artifacts even if ignored compiler outputs have since changed. If no
|
|
114
|
+
set exists, it packages the current inputs. A corrupt or mismatched retained set
|
|
115
|
+
fails; it is never silently replaced. Ordinary packaging also refuses an occupied
|
|
116
|
+
output directory. `--reuse` is available only in release mode.
|
|
117
|
+
|
|
118
|
+
TsPack does not provide remote artifact storage. A workflow must retain and restore
|
|
119
|
+
the directory; rebuilding after losing it cannot reproduce a nondeterministic
|
|
120
|
+
compiler's original bytes. Published assets remain immutable.
|
|
121
|
+
|
|
122
|
+
## TypeScript API
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { TsPack, type ITsPackConfig } from '@git.zone/tspack';
|
|
126
|
+
|
|
127
|
+
const packer = new TsPack('/absolute/project');
|
|
128
|
+
const config: ITsPackConfig = {
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
bundles: [{
|
|
131
|
+
id: 'linux-amd64',
|
|
132
|
+
files: [{ source: 'dist_binary/app', path: 'app', executable: true }]
|
|
133
|
+
}]
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const result = await packer.pack(config, { release: true, reuse: true });
|
|
137
|
+
await packer.verify(result.directory, {
|
|
138
|
+
expectedManifestSha256: result.manifestSha256,
|
|
139
|
+
expectedSourceCommit: result.manifest.sourceCommit,
|
|
140
|
+
requireRelease: true
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Omit the configuration argument to load `.smartconfig.json`. `TsPackError.code`
|
|
145
|
+
identifies configuration, source, input, archive, integrity or occupied-output
|
|
146
|
+
failures. Unexpected filesystem or Git errors remain ordinary errors at the API
|
|
147
|
+
boundary; the CLI reports them without dumping file contents.
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
pnpm install
|
|
153
|
+
pnpm build
|
|
154
|
+
pnpm test
|
|
155
|
+
pnpm exec tsbuild check 'test/**/*'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Tests use disposable Git repositories and real archives. The independent archive
|
|
159
|
+
interoperability check requires the system `tar` executable. Tests neither publish
|
|
160
|
+
releases nor operate on production installations.
|
|
161
|
+
|
|
162
|
+
## License and Legal Information
|
|
163
|
+
|
|
164
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository [license file](./license.md).
|
|
165
|
+
|
|
166
|
+
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
167
|
+
|
|
168
|
+
### Trademarks
|
|
169
|
+
|
|
170
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
|
171
|
+
|
|
172
|
+
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
|
173
|
+
|
|
174
|
+
### Company Information
|
|
175
|
+
|
|
176
|
+
Task Venture Capital GmbH<br>
|
|
177
|
+
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
178
|
+
|
|
179
|
+
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
|
180
|
+
|
|
181
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
package/readme.plan.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# tspack implementation
|
|
2
|
+
|
|
3
|
+
Create the shared owner for packaging existing executables and supporting assets.
|
|
4
|
+
TsDeno and TsRust retain compilation; GitZone and CI retain publication. Pallet
|
|
5
|
+
will consume the published tool instead of maintaining its own archive scripts.
|
|
6
|
+
|
|
7
|
+
- Expose a typed TsPack API and `tspack pack` / `tspack verify` CLI, configured
|
|
8
|
+
under `@git.zone/tspack` in `.smartconfig.json`.
|
|
9
|
+
- Package explicit file sets into deterministic streaming tar.gz bundles using
|
|
10
|
+
SmartArchive's public streaming API. Preserve executable roles, hash every file,
|
|
11
|
+
reject unsafe paths and changed inputs, and publish output directories atomically.
|
|
12
|
+
- Record package/version, Git source identity, configuration identity, bundle
|
|
13
|
+
contents and archive hashes in a sealed artifact set. Release mode requires a
|
|
14
|
+
clean matching tag; development artifacts remain explicitly unpublishable.
|
|
15
|
+
- Verify both archive bytes and every declared member without extracting paths.
|
|
16
|
+
Reuse a retained sealed release set only after verification; never substitute
|
|
17
|
+
freshly rebuilt bytes into an existing immutable release.
|
|
18
|
+
- Include only explicitly selected assets/notices. Do not automatically disclose
|
|
19
|
+
application source, vendor dependency trees or rebuild runtimes for LGPL.
|
|
20
|
+
- Test real archives, deterministic repacking, corruption, unsafe paths, closed
|
|
21
|
+
file sets, stale/dirty release identities and retained-output reuse. Build and
|
|
22
|
+
type-check directly, run stable checker gates, release the tool, then consume it.
|
|
23
|
+
|
|
24
|
+
Global policy now accepts LGPL; its specific obligations remain separate from
|
|
25
|
+
license acceptance. The previous Pallet runtime-reconstruction experiment was
|
|
26
|
+
stopped and its uncommitted distribution prototype removed from active source.
|
package/ts/archive.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { ITsPackArtifact, ITsPackFileRecord } from './interfaces.js';
|
|
3
|
+
import { limits, requireValue, TsPackError, sha256, jsonBytes } from './validation.js';
|
|
4
|
+
|
|
5
|
+
export async function packArchive(directory: string, output: string, files: Record<string, ITsPackFileRecord>): Promise<void> {
|
|
6
|
+
const tar = new plugins.smartarchive.TarTools().getPackStream();
|
|
7
|
+
const written = plugins.streamPromises.pipeline(tar, plugins.zlib.createGzip({ level: 6 }),
|
|
8
|
+
plugins.nodeFs.createWriteStream(output, { flags: 'wx', mode: 0o644 }));
|
|
9
|
+
void written.catch(() => undefined);
|
|
10
|
+
try {
|
|
11
|
+
for (const name of Object.keys(files).sort()) {
|
|
12
|
+
const file = files[name]!;
|
|
13
|
+
const entry = tar.entry({ name, size: file.size, mode: file.mode, mtime: new Date(0), uid: 0, gid: 0, uname: '', gname: '' });
|
|
14
|
+
await plugins.streamPromises.pipeline(plugins.nodeFs.createReadStream(plugins.path.join(directory, name)), entry);
|
|
15
|
+
}
|
|
16
|
+
tar.finalize();
|
|
17
|
+
await written;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
tar.destroy(error instanceof Error ? error : new Error('Archive creation failed.'));
|
|
20
|
+
await written.catch(() => undefined);
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function verifyArchive(file: string, artifact: ITsPackArtifact, expectedMetadata: unknown): Promise<void> {
|
|
26
|
+
const extract = new plugins.smartarchive.TarTools().getExtractStream();
|
|
27
|
+
const seen = new Set<string>();
|
|
28
|
+
extract.on('entry', (header, entry, next) => {
|
|
29
|
+
void (async () => {
|
|
30
|
+
const expected = Object.hasOwn(artifact.files, header.name) ? artifact.files[header.name] : undefined;
|
|
31
|
+
requireValue(header.type === 'file' && expected && !seen.has(header.name) && header.size === expected.size &&
|
|
32
|
+
header.mode === expected.mode, 'archive', 'Unexpected, duplicate or changed archive member.');
|
|
33
|
+
seen.add(header.name);
|
|
34
|
+
const hash = plugins.crypto.createHash('sha256');
|
|
35
|
+
const chunks: Buffer[] = [];
|
|
36
|
+
let bytes = 0;
|
|
37
|
+
for await (const chunk of entry) {
|
|
38
|
+
requireValue(Buffer.isBuffer(chunk), 'archive', 'Expected binary archive content.');
|
|
39
|
+
bytes += chunk.length;
|
|
40
|
+
requireValue(bytes <= expected.size, 'archive', 'Archive member exceeds its declared size.');
|
|
41
|
+
hash.update(chunk);
|
|
42
|
+
if (header.name === 'tspack.json') {
|
|
43
|
+
requireValue(bytes <= limits.json, 'archive', 'Bundle metadata exceeds its limit.');
|
|
44
|
+
chunks.push(chunk);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
requireValue(bytes === expected.size && hash.digest('hex') === expected.sha256, 'integrity', 'Archive member integrity mismatch.');
|
|
48
|
+
if (header.name === 'tspack.json') {
|
|
49
|
+
requireValue(sha256(Buffer.concat(chunks)) === sha256(jsonBytes(expectedMetadata)), 'integrity', 'Bundle and artifact-set metadata disagree.');
|
|
50
|
+
}
|
|
51
|
+
})().then(() => next(), (error: unknown) => extract.destroy(error instanceof Error ? error : new Error('Archive verification failed.')));
|
|
52
|
+
});
|
|
53
|
+
try {
|
|
54
|
+
await plugins.streamPromises.pipeline(plugins.nodeFs.createReadStream(file), plugins.zlib.createGunzip(), extract);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (error instanceof TsPackError) throw error;
|
|
57
|
+
throw new TsPackError('archive', 'Invalid or incomplete archive.');
|
|
58
|
+
}
|
|
59
|
+
requireValue(seen.size === Object.keys(artifact.files).length, 'archive', 'Archive members are missing.');
|
|
60
|
+
}
|