get-pfm 0.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/dist/cjs/__tests__/index.test.cjs +292 -0
  4. package/dist/cjs/__tests__/index.test.cjs.map +1 -0
  5. package/dist/cjs/__tests__/index.test.d.cts +4 -0
  6. package/dist/cjs/checksum.cjs +132 -0
  7. package/dist/cjs/checksum.cjs.map +1 -0
  8. package/dist/cjs/checksum.d.cts +35 -0
  9. package/dist/cjs/convert.cjs +120 -0
  10. package/dist/cjs/convert.cjs.map +1 -0
  11. package/dist/cjs/convert.d.cts +31 -0
  12. package/dist/cjs/index.cjs +45 -0
  13. package/dist/cjs/index.cjs.map +1 -0
  14. package/dist/cjs/index.d.cts +26 -0
  15. package/dist/cjs/parser.cjs +195 -0
  16. package/dist/cjs/parser.cjs.map +1 -0
  17. package/dist/cjs/parser.d.cts +55 -0
  18. package/dist/cjs/serialize.cjs +146 -0
  19. package/dist/cjs/serialize.cjs.map +1 -0
  20. package/dist/cjs/serialize.d.cts +33 -0
  21. package/dist/cjs/types.cjs +29 -0
  22. package/dist/cjs/types.cjs.map +1 -0
  23. package/dist/cjs/types.d.cts +55 -0
  24. package/dist/esm/__tests__/conformance.test.d.ts +9 -0
  25. package/dist/esm/__tests__/conformance.test.d.ts.map +1 -0
  26. package/dist/esm/__tests__/conformance.test.js +170 -0
  27. package/dist/esm/__tests__/conformance.test.js.map +1 -0
  28. package/dist/esm/__tests__/index.test.d.ts +5 -0
  29. package/dist/esm/__tests__/index.test.d.ts.map +1 -0
  30. package/dist/esm/__tests__/index.test.js +287 -0
  31. package/dist/esm/__tests__/index.test.js.map +1 -0
  32. package/dist/esm/checksum.d.ts +36 -0
  33. package/dist/esm/checksum.d.ts.map +1 -0
  34. package/dist/esm/checksum.js +95 -0
  35. package/dist/esm/checksum.js.map +1 -0
  36. package/dist/esm/convert.d.ts +32 -0
  37. package/dist/esm/convert.d.ts.map +1 -0
  38. package/dist/esm/convert.js +115 -0
  39. package/dist/esm/convert.js.map +1 -0
  40. package/dist/esm/index.d.ts +27 -0
  41. package/dist/esm/index.d.ts.map +1 -0
  42. package/dist/esm/index.js +30 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/parser.d.ts +56 -0
  45. package/dist/esm/parser.d.ts.map +1 -0
  46. package/dist/esm/parser.js +189 -0
  47. package/dist/esm/parser.js.map +1 -0
  48. package/dist/esm/serialize.d.ts +34 -0
  49. package/dist/esm/serialize.d.ts.map +1 -0
  50. package/dist/esm/serialize.js +143 -0
  51. package/dist/esm/serialize.js.map +1 -0
  52. package/dist/esm/types.d.ts +56 -0
  53. package/dist/esm/types.d.ts.map +1 -0
  54. package/dist/esm/types.js +26 -0
  55. package/dist/esm/types.js.map +1 -0
  56. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do 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/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # pfm
2
+
3
+ JavaScript/TypeScript library for reading, writing, and validating **.pfm** (Pure Fucking Magic) AI agent output files.
4
+
5
+ Zero dependencies. Works in Node.js 18+ and browsers.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install pfm
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```ts
16
+ import { parse, getSection, validateChecksum } from 'pfm';
17
+ import fs from 'fs';
18
+
19
+ // Parse a .pfm file
20
+ const text = fs.readFileSync('report.pfm', 'utf-8');
21
+ const doc = parse(text);
22
+
23
+ // Access metadata
24
+ console.log(doc.meta.agent); // "claude-code"
25
+ console.log(doc.meta.model); // "claude-opus-4-6"
26
+ console.log(doc.meta.created); // "2026-02-16T..."
27
+
28
+ // Read sections
29
+ console.log(getSection(doc, 'content'));
30
+ console.log(doc.sections.map(s => s.name)); // ["content", "chain", "tools", ...]
31
+
32
+ // Validate checksum
33
+ const result = await validateChecksum(doc);
34
+ console.log(result.valid ? 'VALID' : 'INVALID');
35
+ ```
36
+
37
+ ## API
38
+
39
+ ### Parsing
40
+
41
+ ```ts
42
+ parse(text: string): PFMDocument
43
+ ```
44
+ Parse .pfm text into a structured document.
45
+
46
+ ```ts
47
+ isPFM(text: string): boolean
48
+ ```
49
+ Quick check if text starts with the PFM magic line.
50
+
51
+ ```ts
52
+ getSection(doc: PFMDocument, name: string): string | undefined
53
+ ```
54
+ Get a section's content by name (first match).
55
+
56
+ ```ts
57
+ getSections(doc: PFMDocument, name: string): string[]
58
+ ```
59
+ Get all sections with a given name.
60
+
61
+ ### Checksum
62
+
63
+ ```ts
64
+ computeChecksum(sections: PFMSection[]): Promise<string>
65
+ ```
66
+ Compute SHA-256 checksum of section contents (hex string).
67
+
68
+ ```ts
69
+ validateChecksum(doc: PFMDocument): Promise<ChecksumResult>
70
+ ```
71
+ Validate a document's checksum against its metadata. Fail-closed: returns `{ valid: false }` if no checksum present.
72
+
73
+ ### Serialization
74
+
75
+ ```ts
76
+ serialize(doc: PFMDocument): Promise<string>
77
+ ```
78
+ Serialize a document to .pfm text format. Computes checksum and byte-offset index automatically.
79
+
80
+ ### Conversion
81
+
82
+ ```ts
83
+ toJSON(doc: PFMDocument, indent?: number): string
84
+ fromJSON(json: string): PFMDocument
85
+ toMarkdown(doc: PFMDocument): string
86
+ ```
87
+
88
+ ### Types
89
+
90
+ ```ts
91
+ interface PFMDocument {
92
+ formatVersion: string;
93
+ isStream: boolean;
94
+ meta: PFMMeta;
95
+ sections: PFMSection[];
96
+ }
97
+
98
+ interface PFMSection {
99
+ name: string;
100
+ content: string;
101
+ }
102
+
103
+ interface PFMMeta {
104
+ id?: string;
105
+ agent?: string;
106
+ model?: string;
107
+ created?: string;
108
+ checksum?: string;
109
+ parent?: string;
110
+ tags?: string;
111
+ version?: string;
112
+ [key: string]: string | undefined;
113
+ }
114
+
115
+ interface ChecksumResult {
116
+ valid: boolean;
117
+ expected: string;
118
+ computed: string;
119
+ }
120
+ ```
121
+
122
+ ### Constants
123
+
124
+ ```ts
125
+ META_FIELDS // Record<string, string> — descriptions of standard meta keys
126
+ SECTION_TYPES // Record<string, string> — descriptions of standard section types
127
+ ```
128
+
129
+ ## Browser Usage
130
+
131
+ Works in any modern browser via CDN or bundler:
132
+
133
+ ```html
134
+ <script type="module">
135
+ import { parse, validateChecksum } from 'https://esm.sh/pfm';
136
+
137
+ const response = await fetch('report.pfm');
138
+ const doc = parse(await response.text());
139
+ const { valid } = await validateChecksum(doc);
140
+ </script>
141
+ ```
142
+
143
+ ## CommonJS
144
+
145
+ ```js
146
+ const { parse, getSection } = require('pfm');
147
+ ```
148
+
149
+ ## License
150
+
151
+ MIT
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ /**
3
+ * PFM npm package tests — uses Node.js built-in test runner.
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const node_test_1 = require("node:test");
10
+ const strict_1 = __importDefault(require("node:assert/strict"));
11
+ const index_js_1 = require("../index.cjs");
12
+ const EXAMPLE_PFM = `#!PFM/1.0
13
+ #@meta
14
+ id: abc-123
15
+ agent: test-agent
16
+ model: test-model
17
+ created: 2026-01-01T00:00:00Z
18
+ checksum: placeholder
19
+ tags: test,demo
20
+ version: 1.0
21
+ #@index
22
+ content 200 13
23
+ chain 220 10
24
+ #@content
25
+ Hello, world!
26
+ #@chain
27
+ Some chain
28
+ #!END
29
+ `;
30
+ // ================================================================
31
+ // Parser
32
+ // ================================================================
33
+ (0, node_test_1.describe)('parse', () => {
34
+ (0, node_test_1.it)('parses magic line and format version', () => {
35
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
36
+ strict_1.default.equal(doc.formatVersion, '1.0');
37
+ strict_1.default.equal(doc.isStream, false);
38
+ });
39
+ (0, node_test_1.it)('parses metadata fields', () => {
40
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
41
+ strict_1.default.equal(doc.meta.id, 'abc-123');
42
+ strict_1.default.equal(doc.meta.agent, 'test-agent');
43
+ strict_1.default.equal(doc.meta.model, 'test-model');
44
+ strict_1.default.equal(doc.meta.tags, 'test,demo');
45
+ });
46
+ (0, node_test_1.it)('parses sections in order', () => {
47
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
48
+ strict_1.default.equal(doc.sections.length, 2);
49
+ strict_1.default.equal(doc.sections[0].name, 'content');
50
+ strict_1.default.equal(doc.sections[0].content, 'Hello, world!');
51
+ strict_1.default.equal(doc.sections[1].name, 'chain');
52
+ strict_1.default.equal(doc.sections[1].content, 'Some chain');
53
+ });
54
+ (0, node_test_1.it)('handles stream mode flag', () => {
55
+ const stream = '#!PFM/1.0:STREAM\n#@meta\nagent: x\n#@content\nhi\n#!END\n';
56
+ const doc = (0, index_js_1.parse)(stream);
57
+ strict_1.default.equal(doc.isStream, true);
58
+ strict_1.default.equal(doc.formatVersion, '1.0');
59
+ });
60
+ (0, node_test_1.it)('unescapes content lines', () => {
61
+ const pfm = '#!PFM/1.0\n#@meta\n#@content\nline1\n\\#@fake\n\\#!END\nline4\n#!END\n';
62
+ const doc = (0, index_js_1.parse)(pfm);
63
+ strict_1.default.equal(doc.sections[0].content, 'line1\n#@fake\n#!END\nline4');
64
+ });
65
+ (0, node_test_1.it)('handles missing EOF marker', () => {
66
+ const noeof = '#!PFM/1.0\n#@meta\nagent: x\n#@content\nhello';
67
+ const doc = (0, index_js_1.parse)(noeof);
68
+ strict_1.default.equal(doc.sections.length, 1);
69
+ strict_1.default.equal(doc.sections[0].content, 'hello');
70
+ });
71
+ (0, node_test_1.it)('handles empty sections', () => {
72
+ const pfm = '#!PFM/1.0\n#@meta\n#@content\n#@chain\ndata\n#!END\n';
73
+ const doc = (0, index_js_1.parse)(pfm);
74
+ strict_1.default.equal(doc.sections.length, 2);
75
+ strict_1.default.equal(doc.sections[0].name, 'content');
76
+ strict_1.default.equal(doc.sections[0].content, '');
77
+ strict_1.default.equal(doc.sections[1].content, 'data');
78
+ });
79
+ (0, node_test_1.it)('handles multiline content', () => {
80
+ const pfm = '#!PFM/1.0\n#@meta\n#@content\nline1\nline2\nline3\n#!END\n';
81
+ const doc = (0, index_js_1.parse)(pfm);
82
+ strict_1.default.equal(doc.sections[0].content, 'line1\nline2\nline3');
83
+ });
84
+ (0, node_test_1.it)('handles custom meta fields', () => {
85
+ const pfm = '#!PFM/1.0\n#@meta\nagent: x\ncustom_field: custom_value\n#@content\nhi\n#!END\n';
86
+ const doc = (0, index_js_1.parse)(pfm);
87
+ strict_1.default.equal(doc.meta.custom_field, 'custom_value');
88
+ });
89
+ });
90
+ // ================================================================
91
+ // isPFM
92
+ // ================================================================
93
+ (0, node_test_1.describe)('isPFM', () => {
94
+ (0, node_test_1.it)('returns true for PFM text', () => {
95
+ strict_1.default.equal((0, index_js_1.isPFM)('#!PFM/1.0\n'), true);
96
+ strict_1.default.equal((0, index_js_1.isPFM)('#!PFM/1.0:STREAM\n'), true);
97
+ });
98
+ (0, node_test_1.it)('returns false for non-PFM text', () => {
99
+ strict_1.default.equal((0, index_js_1.isPFM)('not a pfm file'), false);
100
+ strict_1.default.equal((0, index_js_1.isPFM)(''), false);
101
+ strict_1.default.equal((0, index_js_1.isPFM)('{"json": true}'), false);
102
+ });
103
+ });
104
+ // ================================================================
105
+ // getSection / getSections
106
+ // ================================================================
107
+ (0, node_test_1.describe)('getSection', () => {
108
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
109
+ (0, node_test_1.it)('returns section content by name', () => {
110
+ strict_1.default.equal((0, index_js_1.getSection)(doc, 'content'), 'Hello, world!');
111
+ strict_1.default.equal((0, index_js_1.getSection)(doc, 'chain'), 'Some chain');
112
+ });
113
+ (0, node_test_1.it)('returns undefined for missing section', () => {
114
+ strict_1.default.equal((0, index_js_1.getSection)(doc, 'nope'), undefined);
115
+ });
116
+ });
117
+ (0, node_test_1.describe)('getSections', () => {
118
+ (0, node_test_1.it)('returns all sections with given name', () => {
119
+ const pfm = '#!PFM/1.0\n#@meta\n#@content\nfirst\n#@content\nsecond\n#!END\n';
120
+ const doc = (0, index_js_1.parse)(pfm);
121
+ const contents = (0, index_js_1.getSections)(doc, 'content');
122
+ strict_1.default.equal(contents.length, 2);
123
+ strict_1.default.equal(contents[0], 'first');
124
+ strict_1.default.equal(contents[1], 'second');
125
+ });
126
+ (0, node_test_1.it)('returns empty array for missing section', () => {
127
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
128
+ strict_1.default.deepEqual((0, index_js_1.getSections)(doc, 'nope'), []);
129
+ });
130
+ });
131
+ // ================================================================
132
+ // Checksum
133
+ // ================================================================
134
+ (0, node_test_1.describe)('computeChecksum', () => {
135
+ (0, node_test_1.it)('returns hex SHA-256 digest', async () => {
136
+ const hash = await (0, index_js_1.computeChecksum)([{ name: 'content', content: 'hello' }]);
137
+ strict_1.default.equal(typeof hash, 'string');
138
+ strict_1.default.equal(hash.length, 64);
139
+ strict_1.default.match(hash, /^[0-9a-f]{64}$/);
140
+ });
141
+ (0, node_test_1.it)('is deterministic', async () => {
142
+ const sections = [{ name: 'a', content: 'test' }];
143
+ const h1 = await (0, index_js_1.computeChecksum)(sections);
144
+ const h2 = await (0, index_js_1.computeChecksum)(sections);
145
+ strict_1.default.equal(h1, h2);
146
+ });
147
+ (0, node_test_1.it)('changes with different content', async () => {
148
+ const h1 = await (0, index_js_1.computeChecksum)([{ name: 'a', content: 'hello' }]);
149
+ const h2 = await (0, index_js_1.computeChecksum)([{ name: 'a', content: 'world' }]);
150
+ strict_1.default.notEqual(h1, h2);
151
+ });
152
+ });
153
+ (0, node_test_1.describe)('validateChecksum', () => {
154
+ (0, node_test_1.it)('returns valid=false when no checksum in meta', async () => {
155
+ const doc = (0, index_js_1.parse)('#!PFM/1.0\n#@meta\nagent: x\n#@content\nhi\n#!END\n');
156
+ const result = await (0, index_js_1.validateChecksum)(doc);
157
+ strict_1.default.equal(result.valid, false);
158
+ });
159
+ (0, node_test_1.it)('validates a correct checksum', async () => {
160
+ // First compute what the checksum should be
161
+ const hash = await (0, index_js_1.computeChecksum)([{ name: 'content', content: 'hi' }]);
162
+ const pfm = `#!PFM/1.0\n#@meta\nchecksum: ${hash}\n#@content\nhi\n#!END\n`;
163
+ const doc = (0, index_js_1.parse)(pfm);
164
+ const result = await (0, index_js_1.validateChecksum)(doc);
165
+ strict_1.default.equal(result.valid, true);
166
+ strict_1.default.equal(result.expected, hash);
167
+ strict_1.default.equal(result.computed, hash);
168
+ });
169
+ (0, node_test_1.it)('rejects incorrect checksum', async () => {
170
+ const pfm = '#!PFM/1.0\n#@meta\nchecksum: deadbeef\n#@content\nhi\n#!END\n';
171
+ const doc = (0, index_js_1.parse)(pfm);
172
+ const result = await (0, index_js_1.validateChecksum)(doc);
173
+ strict_1.default.equal(result.valid, false);
174
+ strict_1.default.equal(result.expected, 'deadbeef');
175
+ strict_1.default.notEqual(result.computed, 'deadbeef');
176
+ });
177
+ });
178
+ // ================================================================
179
+ // Converters
180
+ // ================================================================
181
+ (0, node_test_1.describe)('toJSON / fromJSON', () => {
182
+ (0, node_test_1.it)('round-trips through JSON', () => {
183
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
184
+ const json = (0, index_js_1.toJSON)(doc);
185
+ const parsed = JSON.parse(json);
186
+ strict_1.default.equal(parsed.pfm_version, '1.0');
187
+ strict_1.default.equal(parsed.meta.agent, 'test-agent');
188
+ strict_1.default.equal(parsed.sections.length, 2);
189
+ strict_1.default.equal(parsed.sections[0].name, 'content');
190
+ });
191
+ (0, node_test_1.it)('fromJSON restores document structure', () => {
192
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
193
+ const json = (0, index_js_1.toJSON)(doc);
194
+ const restored = (0, index_js_1.fromJSON)(json);
195
+ strict_1.default.equal(restored.formatVersion, '1.0');
196
+ strict_1.default.equal(restored.meta.agent, 'test-agent');
197
+ strict_1.default.equal(restored.sections.length, 2);
198
+ strict_1.default.equal(restored.sections[0].content, 'Hello, world!');
199
+ });
200
+ });
201
+ (0, node_test_1.describe)('toMarkdown', () => {
202
+ (0, node_test_1.it)('produces markdown with frontmatter', () => {
203
+ const doc = (0, index_js_1.parse)(EXAMPLE_PFM);
204
+ const md = (0, index_js_1.toMarkdown)(doc);
205
+ strict_1.default.ok(md.startsWith('---\n'));
206
+ strict_1.default.ok(md.includes('agent: test-agent'));
207
+ strict_1.default.ok(md.includes('## content'));
208
+ strict_1.default.ok(md.includes('Hello, world!'));
209
+ strict_1.default.ok(md.includes('## chain'));
210
+ });
211
+ });
212
+ // ================================================================
213
+ // Serialize
214
+ // ================================================================
215
+ (0, node_test_1.describe)('serialize', () => {
216
+ (0, node_test_1.it)('produces valid PFM text', async () => {
217
+ const doc = {
218
+ formatVersion: '1.0',
219
+ isStream: false,
220
+ meta: { agent: 'test', model: 'test-model' },
221
+ sections: [{ name: 'content', content: 'Hello!' }],
222
+ };
223
+ const text = await (0, index_js_1.serialize)(doc);
224
+ strict_1.default.ok(text.startsWith('#!PFM/1.0\n'));
225
+ strict_1.default.ok(text.includes('#@meta\n'));
226
+ strict_1.default.ok(text.includes('agent: test\n'));
227
+ strict_1.default.ok(text.includes('#@content\n'));
228
+ strict_1.default.ok(text.includes('Hello!\n'));
229
+ strict_1.default.ok(text.includes('#!END\n'));
230
+ strict_1.default.ok(text.includes('#@index\n'));
231
+ });
232
+ (0, node_test_1.it)('round-trips parse -> serialize -> parse', async () => {
233
+ const original = {
234
+ formatVersion: '1.0',
235
+ isStream: false,
236
+ meta: { agent: 'roundtrip', model: 'gpt-4' },
237
+ sections: [
238
+ { name: 'content', content: 'Line one\nLine two' },
239
+ { name: 'chain', content: 'User: hello\nAgent: hi' },
240
+ ],
241
+ };
242
+ const text = await (0, index_js_1.serialize)(original);
243
+ const restored = (0, index_js_1.parse)(text);
244
+ strict_1.default.equal(restored.meta.agent, 'roundtrip');
245
+ strict_1.default.equal(restored.sections.length, 2);
246
+ strict_1.default.equal(restored.sections[0].content, 'Line one\nLine two');
247
+ strict_1.default.equal(restored.sections[1].content, 'User: hello\nAgent: hi');
248
+ });
249
+ (0, node_test_1.it)('serialized checksum validates', async () => {
250
+ const doc = {
251
+ formatVersion: '1.0',
252
+ isStream: false,
253
+ meta: { agent: 'test' },
254
+ sections: [{ name: 'content', content: 'Check me' }],
255
+ };
256
+ const text = await (0, index_js_1.serialize)(doc);
257
+ const parsed = (0, index_js_1.parse)(text);
258
+ const result = await (0, index_js_1.validateChecksum)(parsed);
259
+ strict_1.default.equal(result.valid, true);
260
+ });
261
+ (0, node_test_1.it)('escapes dangerous content lines', async () => {
262
+ const doc = {
263
+ formatVersion: '1.0',
264
+ isStream: false,
265
+ meta: {},
266
+ sections: [{ name: 'content', content: '#@fake\n#!END\nnormal line' }],
267
+ };
268
+ const text = await (0, index_js_1.serialize)(doc);
269
+ strict_1.default.ok(text.includes('\\#@fake'));
270
+ strict_1.default.ok(text.includes('\\#!END'));
271
+ // Verify round-trip preserves the dangerous content
272
+ const parsed = (0, index_js_1.parse)(text);
273
+ strict_1.default.equal(parsed.sections[0].content, '#@fake\n#!END\nnormal line');
274
+ });
275
+ });
276
+ // ================================================================
277
+ // Constants
278
+ // ================================================================
279
+ (0, node_test_1.describe)('constants', () => {
280
+ (0, node_test_1.it)('META_FIELDS has standard keys', () => {
281
+ strict_1.default.ok('id' in index_js_1.META_FIELDS);
282
+ strict_1.default.ok('agent' in index_js_1.META_FIELDS);
283
+ strict_1.default.ok('checksum' in index_js_1.META_FIELDS);
284
+ });
285
+ (0, node_test_1.it)('SECTION_TYPES has standard sections', () => {
286
+ strict_1.default.ok('content' in index_js_1.SECTION_TYPES);
287
+ strict_1.default.ok('chain' in index_js_1.SECTION_TYPES);
288
+ strict_1.default.ok('tools' in index_js_1.SECTION_TYPES);
289
+ strict_1.default.ok('metrics' in index_js_1.SECTION_TYPES);
290
+ });
291
+ });
292
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../src/__tests__/index.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * PFM npm package tests — uses Node.js built-in test runner.
3
+ */
4
+ export {};
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ /**
3
+ * PFM Checksum — SHA-256 validation for PFM documents.
4
+ *
5
+ * Works in both Node.js (crypto module) and browsers (Web Crypto API).
6
+ * Mirrors the Python PFMDocument.compute_checksum() logic exactly:
7
+ * - Iterate sections in order
8
+ * - Hash each section's unescaped content as UTF-8 bytes
9
+ * - Compare hex digest to meta.checksum
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.computeChecksum = computeChecksum;
46
+ exports.validateChecksum = validateChecksum;
47
+ /**
48
+ * Compute the SHA-256 checksum of a document's section contents.
49
+ *
50
+ * @param sections - Array of PFM sections.
51
+ * @returns Hex-encoded SHA-256 hash string.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { parse, computeChecksum } from 'pfm';
56
+ *
57
+ * const doc = parse(text);
58
+ * const hash = await computeChecksum(doc.sections);
59
+ * console.log(hash === doc.meta.checksum); // true if valid
60
+ * ```
61
+ */
62
+ async function computeChecksum(sections) {
63
+ // Detect environment and use the appropriate crypto API
64
+ if (typeof globalThis !== 'undefined' && globalThis.crypto?.subtle) {
65
+ return checksumWebCrypto(sections);
66
+ }
67
+ return checksumNode(sections);
68
+ }
69
+ /**
70
+ * Validate a document's checksum against its metadata.
71
+ *
72
+ * Fail-closed: returns `{ valid: false }` if no checksum is present in meta.
73
+ *
74
+ * @param doc - A parsed PFMDocument.
75
+ * @returns ChecksumResult with valid, expected, and computed fields.
76
+ */
77
+ async function validateChecksum(doc) {
78
+ const expected = doc.meta.checksum || '';
79
+ const computed = await computeChecksum(doc.sections);
80
+ return {
81
+ valid: expected !== '' && timingSafeEqual(computed, expected),
82
+ expected,
83
+ computed,
84
+ };
85
+ }
86
+ /** Constant-time string comparison to prevent timing side-channels. */
87
+ function timingSafeEqual(a, b) {
88
+ const len = Math.max(a.length, b.length);
89
+ let result = a.length === b.length ? 0 : 1;
90
+ for (let i = 0; i < len; i++) {
91
+ result |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
92
+ }
93
+ return result === 0;
94
+ }
95
+ /** Web Crypto API implementation (browsers + Node 18+). */
96
+ async function checksumWebCrypto(sections) {
97
+ const encoder = new TextEncoder();
98
+ const chunks = [];
99
+ let totalLen = 0;
100
+ for (const section of sections) {
101
+ const encoded = encoder.encode(section.content);
102
+ chunks.push(encoded);
103
+ totalLen += encoded.length;
104
+ }
105
+ // Concatenate all chunks
106
+ const merged = new Uint8Array(totalLen);
107
+ let offset = 0;
108
+ for (const chunk of chunks) {
109
+ merged.set(chunk, offset);
110
+ offset += chunk.length;
111
+ }
112
+ const hashBuffer = await crypto.subtle.digest('SHA-256', merged);
113
+ return hexEncode(new Uint8Array(hashBuffer));
114
+ }
115
+ /** Node.js crypto module implementation. */
116
+ async function checksumNode(sections) {
117
+ const { createHash } = await Promise.resolve().then(() => __importStar(require('crypto')));
118
+ const hash = createHash('sha256');
119
+ for (const section of sections) {
120
+ hash.update(section.content, 'utf8');
121
+ }
122
+ return hash.digest('hex');
123
+ }
124
+ /** Convert Uint8Array to hex string. */
125
+ function hexEncode(bytes) {
126
+ let hex = '';
127
+ for (let i = 0; i < bytes.length; i++) {
128
+ hex += bytes[i].toString(16).padStart(2, '0');
129
+ }
130
+ return hex;
131
+ }
132
+ //# sourceMappingURL=checksum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checksum.js","sourceRoot":"","sources":["../../src/checksum.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * PFM Checksum — SHA-256 validation for PFM documents.
3
+ *
4
+ * Works in both Node.js (crypto module) and browsers (Web Crypto API).
5
+ * Mirrors the Python PFMDocument.compute_checksum() logic exactly:
6
+ * - Iterate sections in order
7
+ * - Hash each section's unescaped content as UTF-8 bytes
8
+ * - Compare hex digest to meta.checksum
9
+ */
10
+ import type { PFMDocument, PFMSection, ChecksumResult } from './types.js';
11
+ /**
12
+ * Compute the SHA-256 checksum of a document's section contents.
13
+ *
14
+ * @param sections - Array of PFM sections.
15
+ * @returns Hex-encoded SHA-256 hash string.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { parse, computeChecksum } from 'pfm';
20
+ *
21
+ * const doc = parse(text);
22
+ * const hash = await computeChecksum(doc.sections);
23
+ * console.log(hash === doc.meta.checksum); // true if valid
24
+ * ```
25
+ */
26
+ export declare function computeChecksum(sections: PFMSection[]): Promise<string>;
27
+ /**
28
+ * Validate a document's checksum against its metadata.
29
+ *
30
+ * Fail-closed: returns `{ valid: false }` if no checksum is present in meta.
31
+ *
32
+ * @param doc - A parsed PFMDocument.
33
+ * @returns ChecksumResult with valid, expected, and computed fields.
34
+ */
35
+ export declare function validateChecksum(doc: PFMDocument): Promise<ChecksumResult>;