@parmanasystems/bundle 1.0.19

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/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @parmanasystems/bundle
2
+
3
+ Deterministic artifact canonicalization, hashing, and bundle I/O for the parmanasystems governance runtime.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@parmanasystems/bundle)](https://www.npmjs.com/package/@parmanasystems/bundle)
6
+
7
+ ## Overview
8
+
9
+ `@parmanasystems/bundle` provides the canonical serialization foundation that makes parmanasystems governance decisions independently verifiable.
10
+
11
+ Every signed governance artifact — execution results, runtime manifests, policy bundles — is signed over the **canonical JSON** form produced by this package. This ensures:
12
+
13
+ - The same object always serializes to the same byte sequence regardless of property insertion order
14
+ - Signatures can be independently reproduced and verified by any party
15
+ - Artifact hashes are stable across platforms and runtimes
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @parmanasystems/bundle
21
+ ```
22
+
23
+ ## API
24
+
25
+ ### `canonicalize(value: unknown): string`
26
+
27
+ Produces a deterministic JSON string with recursively sorted keys and stable formatting (INV-001). Enforced as a sealed-VM function — no `Date.now` or `Math.random` allowed inside.
28
+
29
+ ```typescript
30
+ import { canonicalize } from "@parmanasystems/bundle";
31
+
32
+ canonicalize({ b: 2, a: 1 });
33
+ // '{\n "a": 1,\n "b": 2\n}'
34
+ ```
35
+
36
+ ### `sha256(content: string): string`
37
+
38
+ Returns a SHA-256 hex digest of the given string.
39
+
40
+ ```typescript
41
+ import { sha256 } from "@parmanasystems/bundle";
42
+
43
+ sha256('{"a":1}');
44
+ // "e3b0c44..." (hex)
45
+ ```
46
+
47
+ ### `generateManifest(policyId, version, directory): Promise<BundleManifest>`
48
+
49
+ Hashes all files in a policy directory and produces a `BundleManifest` with a self-verifying `bundle_hash`.
50
+
51
+ ```typescript
52
+ import { generateManifest } from "@parmanasystems/bundle";
53
+
54
+ const manifest = await generateManifest("claims-approval", "v1", "./policies/claims-approval/v1");
55
+ ```
56
+
57
+ ### `readManifest(directory): Promise<BundleManifest>`
58
+
59
+ Reads `bundle.manifest.json` from a directory.
60
+
61
+ ### `verifyManifest(manifest, directory): Promise<VerifyResult>`
62
+
63
+ Re-hashes all artifacts and compares against the stored manifest.
64
+
65
+ ```typescript
66
+ const result = await verifyManifest(manifest, directory);
67
+ console.log(result.valid); // true
68
+ ```
69
+
70
+ ### `traverseBundle(dir: string): Promise<string[]>`
71
+
72
+ Returns sorted POSIX-relative file paths within a bundle directory, enabling deterministic hash computation.
73
+
74
+ ## Types
75
+
76
+ ### BundleManifest
77
+
78
+ ```typescript
79
+ interface BundleManifest {
80
+ manifest_version: "1";
81
+ policy_id: string;
82
+ policy_version: string;
83
+ artifacts: BundleArtifact[];
84
+ runtime_requirements: RuntimeRequirements;
85
+ bundle_hash: string; // self-verifying SHA-256
86
+ }
87
+
88
+ interface BundleArtifact {
89
+ path: string; // POSIX-relative
90
+ hash: string; // SHA-256 hex
91
+ }
92
+ ```
93
+
94
+ ### VerifyResult
95
+
96
+ ```typescript
97
+ interface VerifyResult {
98
+ valid: boolean;
99
+ expected_bundle_hash: string;
100
+ actual_bundle_hash: string;
101
+ }
102
+ ```
103
+
104
+ ## Role in the pipeline
105
+
106
+ ```
107
+ Governance artifact (object)
108
+
109
+ canonicalize() ← this package
110
+
111
+ canonical JSON string
112
+
113
+ sha256() / Ed25519 sign / verify
114
+ ```
115
+
116
+ `canonicalize()` is called by `signExecutionToken()`, `stageSign()`, and every other signing path in `@parmanasystems/execution`. You only need to call it directly for custom signing or verification flows.
117
+
118
+ ## License
119
+
120
+ Apache-2.0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Serializes `value` to a stable, pretty-printed JSON string with object keys
3
+ * sorted recursively. This is the canonical byte representation used for all
4
+ * bundle hashing and manifest signing operations.
5
+ *
6
+ * Arrays preserve their original order; only object keys are sorted.
7
+ *
8
+ * Enforces: INV-001, INV-048, INV-049, INV-050, INV-051, INV-052, INV-053, INV-054.
9
+ */
10
+ declare function canonicalize(value: unknown): string;
11
+
12
+ /** Returns the SHA-256 hex digest of `content` (interpreted as UTF-8). Enforces: INV-047, INV-057. */
13
+ declare function sha256(content: string): string;
14
+
15
+ /** Capability and version constraints that a runtime must satisfy to execute a bundle. */
16
+ interface RuntimeRequirements {
17
+ /** Runtime capability strings that must appear in the executing runtime's capability list (e.g. `"replay-protection"`). */
18
+ required_capabilities: string[];
19
+ /** Runtime version strings (e.g. `"1.0.0"`) that are acceptable to the bundle. */
20
+ supported_runtime_versions: string[];
21
+ /** Schema version strings (e.g. `"1.0.0"`) that the bundle's artifacts conform to. */
22
+ supported_schema_versions: string[];
23
+ }
24
+
25
+ /** A single file artifact tracked by a BundleManifest, identified by its relative path and SHA-256 hash. */
26
+ interface BundleArtifact {
27
+ /** Normalized POSIX-style path relative to the bundle directory. */
28
+ path: string;
29
+ /** SHA-256 hex digest of the canonical file content. */
30
+ hash: string;
31
+ }
32
+ /**
33
+ * Signed, content-addressed manifest describing all artifacts in a policy bundle.
34
+ * The `bundle_hash` is a SHA-256 commitment over the full manifest (with `bundle_hash`
35
+ * zeroed), making the manifest self-verifying.
36
+ */
37
+ interface BundleManifest {
38
+ /** Manifest format version. Currently `"1"`. */
39
+ manifest_version: string;
40
+ /** Policy identifier this bundle belongs to. */
41
+ policy_id: string;
42
+ /** Policy version string (e.g. `"v1"`). */
43
+ policy_version: string;
44
+ /** Ordered list of all content-addressed artifacts in the bundle. */
45
+ artifacts: BundleArtifact[];
46
+ /** Runtime capability and version constraints required to execute this bundle. */
47
+ runtime_requirements: RuntimeRequirements;
48
+ /**
49
+ * SHA-256 hex digest of the canonical manifest with `bundle_hash` set to `""`.
50
+ * A tamper-evident commitment over all artifact hashes and metadata.
51
+ */
52
+ bundle_hash: string;
53
+ }
54
+ /** Result returned by {@link verifyManifest}, including the expected and actual bundle hashes. */
55
+ interface VerifyResult {
56
+ /** `true` when the re-computed bundle hash matches the manifest commitment. */
57
+ valid: boolean;
58
+ /** The bundle hash stored in the manifest (expected value). */
59
+ expected_bundle_hash: string;
60
+ /** The bundle hash re-computed from the directory contents (actual value). */
61
+ actual_bundle_hash: string;
62
+ }
63
+
64
+ /**
65
+ * Generates a content-addressed {@link BundleManifest} for `policyId`/`policyVersion`
66
+ * by hashing every file under `directory` (manifest and sig files excluded).
67
+ *
68
+ * JSON files are canonicalized before hashing; other files have CRLF normalized
69
+ * to LF. The final `bundle_hash` is SHA-256 of the canonical manifest with
70
+ * `bundle_hash` set to `""`, making it a deterministic commitment over all
71
+ * artifact content.
72
+ *
73
+ * @param policyId - Policy identifier to embed in the manifest.
74
+ * @param policyVersion - Policy version string (e.g. `"v1"`).
75
+ * @param directory - Absolute path to the bundle directory.
76
+ */
77
+ declare function generateManifest(policyId: string, policyVersion: string, directory: string): BundleManifest;
78
+
79
+ /**
80
+ * Reads and JSON-parses `bundle.manifest.json` from `directory`.
81
+ *
82
+ * @param directory - Path to a bundle directory containing `bundle.manifest.json`.
83
+ */
84
+ declare function readManifest(directory: string): BundleManifest;
85
+
86
+ /**
87
+ * Recursively enumerates all non-excluded files under `directory`, returning
88
+ * normalized POSIX-style paths relative to `root` (defaults to `directory`).
89
+ * Entries are sorted deterministically by name at each level.
90
+ * `bundle.manifest.json` and `bundle.sig` are always excluded.
91
+ *
92
+ * @param directory - The directory to traverse.
93
+ * @param root - The root used for computing relative output paths.
94
+ */
95
+ declare function traverseDirectory(directory: string, root?: string): string[];
96
+
97
+ /**
98
+ * Re-hashes every file under `directory` and recomputes the bundle hash, then
99
+ * compares it against `manifest.bundle_hash`.
100
+ *
101
+ * Returns `valid: true` only when the on-disk content matches the manifest
102
+ * commitment exactly. Any file addition, deletion, or modification produces
103
+ * a differing hash and `valid: false`.
104
+ *
105
+ * @param manifest - The reference manifest containing the expected bundle hash.
106
+ * @param directory - Bundle directory whose contents will be re-hashed.
107
+ */
108
+ declare function verifyManifest(manifest: BundleManifest, directory: string): VerifyResult;
109
+
110
+ /**
111
+ * Writes `manifest` to `<directory>/bundle.manifest.json` in canonical form.
112
+ * The output is deterministic: identical manifests always produce identical
113
+ * bytes on disk.
114
+ *
115
+ * @param manifest - The manifest to persist.
116
+ * @param directory - Destination bundle directory.
117
+ */
118
+ declare function writeManifest(manifest: BundleManifest, directory: string): void;
119
+
120
+ export { type BundleArtifact, type BundleManifest, type RuntimeRequirements, type VerifyResult, canonicalize, generateManifest, readManifest, sha256, traverseDirectory, verifyManifest, writeManifest };
package/dist/index.js ADDED
@@ -0,0 +1,219 @@
1
+ // src/canonicalize.ts
2
+ function canonicalize(value) {
3
+ return JSON.stringify(sortKeys(value), null, 2);
4
+ }
5
+ function sortKeys(value) {
6
+ if (Array.isArray(value)) {
7
+ return value.map(sortKeys);
8
+ }
9
+ if (value !== null && typeof value === "object") {
10
+ return Object.keys(value).sort().reduce((acc, key) => {
11
+ acc[key] = sortKeys(
12
+ value[key]
13
+ );
14
+ return acc;
15
+ }, {});
16
+ }
17
+ return value;
18
+ }
19
+
20
+ // src/hash.ts
21
+ import * as crypto from "crypto";
22
+ function sha256(content) {
23
+ return crypto.createHash("sha256").update(content, "utf8").digest("hex");
24
+ }
25
+
26
+ // src/manifest.ts
27
+ import * as fs2 from "fs";
28
+ import * as path2 from "path";
29
+
30
+ // src/traverse.ts
31
+ import fs from "fs";
32
+ import path from "path";
33
+ var EXCLUDED_FILES = [
34
+ "bundle.manifest.json",
35
+ "bundle.sig"
36
+ ];
37
+ function traverseDirectory(directory, root = directory) {
38
+ const entries = fs.readdirSync(directory, {
39
+ withFileTypes: true
40
+ }).sort(
41
+ (a, b) => a.name.localeCompare(b.name)
42
+ );
43
+ const results = [];
44
+ for (const entry of entries) {
45
+ const fullPath = path.join(
46
+ directory,
47
+ entry.name
48
+ );
49
+ if (entry.isDirectory()) {
50
+ results.push(
51
+ ...traverseDirectory(
52
+ fullPath,
53
+ root
54
+ )
55
+ );
56
+ continue;
57
+ }
58
+ if (EXCLUDED_FILES.includes(
59
+ entry.name
60
+ )) {
61
+ continue;
62
+ }
63
+ const relativePath = path.relative(
64
+ root,
65
+ fullPath
66
+ );
67
+ results.push(
68
+ normalizePath(relativePath)
69
+ );
70
+ }
71
+ return results;
72
+ }
73
+ function normalizePath(value) {
74
+ return value.replace(/\\\\/g, "/");
75
+ }
76
+
77
+ // src/manifest.ts
78
+ function generateManifest(policyId, policyVersion, directory) {
79
+ const files = traverseDirectory(directory);
80
+ const artifacts = files.map((relativePath) => {
81
+ const fullPath = path2.join(
82
+ directory,
83
+ relativePath
84
+ );
85
+ const content = fs2.readFileSync(
86
+ fullPath,
87
+ "utf8"
88
+ );
89
+ const canonicalContent = relativePath.endsWith(".json") ? canonicalize(
90
+ JSON.parse(content)
91
+ ) : content.replace(
92
+ /\r\n/g,
93
+ "\n"
94
+ );
95
+ return {
96
+ path: relativePath,
97
+ hash: sha256(
98
+ canonicalContent
99
+ )
100
+ };
101
+ });
102
+ const manifest = {
103
+ manifest_version: "1",
104
+ policy_id: policyId,
105
+ policy_version: policyVersion,
106
+ artifacts,
107
+ runtime_requirements: {
108
+ required_capabilities: [
109
+ "replay-protection",
110
+ "attestation-signing",
111
+ "bundle-verification"
112
+ ],
113
+ supported_runtime_versions: [
114
+ "1.0.0"
115
+ ],
116
+ supported_schema_versions: [
117
+ "1.0.0"
118
+ ]
119
+ },
120
+ bundle_hash: ""
121
+ };
122
+ const canonical = canonicalize(
123
+ manifest
124
+ );
125
+ const bundleHash = sha256(
126
+ canonical
127
+ );
128
+ manifest.bundle_hash = bundleHash;
129
+ return manifest;
130
+ }
131
+
132
+ // src/read.ts
133
+ import fs3 from "fs";
134
+ import path3 from "path";
135
+ function readManifest(directory) {
136
+ const manifestPath = path3.join(
137
+ directory,
138
+ "bundle.manifest.json"
139
+ );
140
+ const content = fs3.readFileSync(
141
+ manifestPath,
142
+ "utf8"
143
+ );
144
+ return JSON.parse(content);
145
+ }
146
+
147
+ // src/verify.ts
148
+ import fs4 from "fs";
149
+ import path4 from "path";
150
+ function verifyManifest(manifest, directory) {
151
+ const files = traverseDirectory(directory);
152
+ const recalculatedArtifacts = files.map((relativePath) => {
153
+ const fullPath = path4.join(
154
+ directory,
155
+ relativePath
156
+ );
157
+ const content = fs4.readFileSync(
158
+ fullPath,
159
+ "utf8"
160
+ );
161
+ const canonicalContent = relativePath.endsWith(".json") ? canonicalize(
162
+ JSON.parse(content)
163
+ ) : content.replace(
164
+ /\r\n/g,
165
+ "\n"
166
+ );
167
+ return {
168
+ path: relativePath,
169
+ hash: sha256(
170
+ canonicalContent
171
+ )
172
+ };
173
+ });
174
+ const reconstructedManifest = {
175
+ manifest_version: manifest.manifest_version,
176
+ policy_id: manifest.policy_id,
177
+ policy_version: manifest.policy_version,
178
+ artifacts: recalculatedArtifacts,
179
+ runtime_requirements: manifest.runtime_requirements,
180
+ bundle_hash: ""
181
+ };
182
+ const canonical = canonicalize(
183
+ reconstructedManifest
184
+ );
185
+ const recalculatedBundleHash = sha256(
186
+ canonical
187
+ );
188
+ return {
189
+ valid: recalculatedBundleHash === manifest.bundle_hash,
190
+ expected_bundle_hash: manifest.bundle_hash,
191
+ actual_bundle_hash: recalculatedBundleHash
192
+ };
193
+ }
194
+
195
+ // src/write.ts
196
+ import fs5 from "fs";
197
+ import path5 from "path";
198
+ function writeManifest(manifest, directory) {
199
+ const manifestPath = path5.join(
200
+ directory,
201
+ "bundle.manifest.json"
202
+ );
203
+ const canonical = canonicalize(manifest);
204
+ fs5.writeFileSync(
205
+ manifestPath,
206
+ canonical,
207
+ "utf8"
208
+ );
209
+ }
210
+ export {
211
+ canonicalize,
212
+ generateManifest,
213
+ readManifest,
214
+ sha256,
215
+ traverseDirectory,
216
+ verifyManifest,
217
+ writeManifest
218
+ };
219
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/canonicalize.ts","../src/hash.ts","../src/manifest.ts","../src/traverse.ts","../src/read.ts","../src/verify.ts","../src/write.ts"],"sourcesContent":["/**\n * Serializes `value` to a stable, pretty-printed JSON string with object keys\n * sorted recursively. This is the canonical byte representation used for all\n * bundle hashing and manifest signing operations.\n *\n * Arrays preserve their original order; only object keys are sorted.\n *\n * Enforces: INV-001, INV-048, INV-049, INV-050, INV-051, INV-052, INV-053, INV-054.\n */\nexport function canonicalize(value: unknown): string {\n return JSON.stringify(sortKeys(value), null, 2);\n}\n\nfunction sortKeys(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortKeys);\n }\n\n if (value !== null && typeof value === \"object\") {\n return Object.keys(value as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = sortKeys(\n (value as Record<string, unknown>)[key]\n );\n\n return acc;\n }, {});\n }\n\n return value;\n}\n\n\n\n\n","import * as crypto from \"crypto\";\n\n/** Returns the SHA-256 hex digest of `content` (interpreted as UTF-8). Enforces: INV-047, INV-057. */\nexport function sha256(content: string): string {\n return crypto\n .createHash(\"sha256\")\n .update(content, \"utf8\")\n .digest(\"hex\");\n}\n\n\n\n\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport { canonicalize } from \"./canonicalize\";\nimport { sha256 } from \"./hash\";\nimport { traverseDirectory } from \"./traverse\";\n\nimport type {\n BundleArtifact,\n BundleManifest,\n} from \"./types\";\n\n/**\n * Generates a content-addressed {@link BundleManifest} for `policyId`/`policyVersion`\n * by hashing every file under `directory` (manifest and sig files excluded).\n *\n * JSON files are canonicalized before hashing; other files have CRLF normalized\n * to LF. The final `bundle_hash` is SHA-256 of the canonical manifest with\n * `bundle_hash` set to `\"\"`, making it a deterministic commitment over all\n * artifact content.\n *\n * @param policyId - Policy identifier to embed in the manifest.\n * @param policyVersion - Policy version string (e.g. `\"v1\"`).\n * @param directory - Absolute path to the bundle directory.\n */\nexport function generateManifest(\n policyId: string,\n policyVersion: string,\n directory: string\n): BundleManifest {\n\n const files =\n traverseDirectory(directory);\n\n const artifacts: BundleArtifact[] =\n files.map((relativePath) => {\n\n const fullPath =\n path.join(\n directory,\n relativePath\n );\n\n const content =\n fs.readFileSync(\n fullPath,\n \"utf8\"\n );\n\n const canonicalContent =\n relativePath.endsWith(\".json\")\n ? canonicalize(\n JSON.parse(content)\n )\n : content.replace(\n /\\r\\n/g,\n \"\\n\"\n );\n\n return {\n path:\n relativePath,\n\n hash:\n sha256(\n canonicalContent\n ),\n };\n });\n\n const manifest: BundleManifest = {\n manifest_version:\n \"1\",\n\n policy_id:\n policyId,\n\n policy_version:\n policyVersion,\n\n artifacts,\n\n runtime_requirements: {\n required_capabilities: [\n \"replay-protection\",\n \"attestation-signing\",\n \"bundle-verification\",\n ],\n\n supported_runtime_versions: [\n \"1.0.0\",\n ],\n\n supported_schema_versions: [\n \"1.0.0\",\n ],\n },\n\n bundle_hash:\n \"\",\n };\n\n const canonical =\n canonicalize(\n manifest\n );\n\n const bundleHash =\n sha256(\n canonical\n );\n\n manifest.bundle_hash =\n bundleHash;\n\n return manifest;\n}\n\n\n\n\n","import fs from \"fs\";\nimport path from \"path\";\n\nconst EXCLUDED_FILES = [\n \"bundle.manifest.json\",\n \"bundle.sig\",\n];\n\n/**\n * Recursively enumerates all non-excluded files under `directory`, returning\n * normalized POSIX-style paths relative to `root` (defaults to `directory`).\n * Entries are sorted deterministically by name at each level.\n * `bundle.manifest.json` and `bundle.sig` are always excluded.\n *\n * @param directory - The directory to traverse.\n * @param root - The root used for computing relative output paths.\n */\nexport function traverseDirectory(\n directory: string,\n root: string = directory\n): string[] {\n const entries = fs\n .readdirSync(directory, {\n withFileTypes: true,\n })\n .sort((a, b) =>\n a.name.localeCompare(b.name)\n );\n\n const results: string[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(\n directory,\n entry.name\n );\n\n if (entry.isDirectory()) {\n results.push(\n ...traverseDirectory(\n fullPath,\n root\n )\n );\n\n continue;\n }\n\n if (\n EXCLUDED_FILES.includes(\n entry.name\n )\n ) {\n continue;\n }\n\n const relativePath =\n path.relative(\n root,\n fullPath\n );\n\n results.push(\n normalizePath(relativePath)\n );\n }\n\n return results;\n}\n\nfunction normalizePath(\n value: string\n): string {\n return value.replace(/\\\\\\\\/g, \"/\");\n}\n\n\n\n\n","import fs from \"fs\";\nimport path from \"path\";\n\nimport type {\n BundleManifest,\n} from \"./types\";\n\n/**\n * Reads and JSON-parses `bundle.manifest.json` from `directory`.\n *\n * @param directory - Path to a bundle directory containing `bundle.manifest.json`.\n */\nexport function readManifest(\n directory: string\n): BundleManifest {\n const manifestPath = path.join(\n directory,\n \"bundle.manifest.json\"\n );\n\n const content =\n fs.readFileSync(\n manifestPath,\n \"utf8\"\n );\n\n return JSON.parse(content);\n}\n\n\n\n\n","import fs from \"fs\";\nimport path from \"path\";\n\nimport { canonicalize } from \"./canonicalize\";\nimport { sha256 } from \"./hash\";\nimport { traverseDirectory } from \"./traverse\";\n\nimport type {\n BundleManifest,\n VerifyResult,\n} from \"./types\";\n\n/**\n * Re-hashes every file under `directory` and recomputes the bundle hash, then\n * compares it against `manifest.bundle_hash`.\n *\n * Returns `valid: true` only when the on-disk content matches the manifest\n * commitment exactly. Any file addition, deletion, or modification produces\n * a differing hash and `valid: false`.\n *\n * @param manifest - The reference manifest containing the expected bundle hash.\n * @param directory - Bundle directory whose contents will be re-hashed.\n */\nexport function verifyManifest(\n manifest: BundleManifest,\n directory: string\n): VerifyResult {\n\n const files =\n traverseDirectory(directory);\n\n const recalculatedArtifacts =\n files.map((relativePath) => {\n\n const fullPath =\n path.join(\n directory,\n relativePath\n );\n\n const content =\n fs.readFileSync(\n fullPath,\n \"utf8\"\n );\n\n const canonicalContent =\n relativePath.endsWith(\".json\")\n ? canonicalize(\n JSON.parse(content)\n )\n : content.replace(\n /\\r\\n/g,\n \"\\n\"\n );\n\n return {\n path: relativePath,\n\n hash:\n sha256(\n canonicalContent\n ),\n };\n });\n\n const reconstructedManifest: BundleManifest =\n {\n manifest_version:\n manifest.manifest_version,\n\n policy_id:\n manifest.policy_id,\n\n policy_version:\n manifest.policy_version,\n\n artifacts:\n recalculatedArtifacts,\n\n runtime_requirements:\n manifest.runtime_requirements,\n\n bundle_hash: \"\",\n };\n\n const canonical =\n canonicalize(\n reconstructedManifest\n );\n\n const recalculatedBundleHash =\n sha256(\n canonical\n );\n\n return {\n valid:\n recalculatedBundleHash ===\n manifest.bundle_hash,\n\n expected_bundle_hash:\n manifest.bundle_hash,\n\n actual_bundle_hash:\n recalculatedBundleHash,\n };\n}\n\n\n\n\n","import fs from \"fs\";\nimport path from \"path\";\n\nimport { canonicalize } from \"./canonicalize\";\n\nimport type {\n BundleManifest,\n} from \"./types\";\n\n/**\n * Writes `manifest` to `<directory>/bundle.manifest.json` in canonical form.\n * The output is deterministic: identical manifests always produce identical\n * bytes on disk.\n *\n * @param manifest - The manifest to persist.\n * @param directory - Destination bundle directory.\n */\nexport function writeManifest(\n manifest: BundleManifest,\n directory: string\n): void {\n const manifestPath = path.join(\n directory,\n \"bundle.manifest.json\"\n );\n\n const canonical =\n canonicalize(manifest);\n\n fs.writeFileSync(\n manifestPath,\n canonical,\n \"utf8\"\n );\n}\n\n\n\n\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","path","fs","fs","path","path","fs"]}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@parmanasystems/bundle",
3
+ "version": "1.0.19",
4
+ "private": false,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsup"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "description": "Deterministic governance artifact packaging and manifest infrastructure for parmanasystems.",
21
+ "license": "Apache-2.0",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/pavancharak/parmanasystems-core.git"
25
+ },
26
+ "homepage": "https://github.com/pavancharak/parmanasystems-core",
27
+ "bugs": {
28
+ "url": "https://github.com/pavancharak/parmanasystems-core/issues"
29
+ },
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "keywords": [
34
+ "governance",
35
+ "bundle",
36
+ "manifest",
37
+ "deterministic",
38
+ "provenance",
39
+ "artifacts"
40
+ ],
41
+ "main": "./dist/index.js",
42
+ "types": "./dist/index.d.ts"
43
+ }