@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.
@@ -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.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@git.zone/tspack',
6
+ version: '1.1.0',
7
+ description: 'Verified executable and asset bundles for GitZone projects.'
8
+ }
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
+ }