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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/cjs/__tests__/index.test.cjs +292 -0
- package/dist/cjs/__tests__/index.test.cjs.map +1 -0
- package/dist/cjs/__tests__/index.test.d.cts +4 -0
- package/dist/cjs/checksum.cjs +132 -0
- package/dist/cjs/checksum.cjs.map +1 -0
- package/dist/cjs/checksum.d.cts +35 -0
- package/dist/cjs/convert.cjs +120 -0
- package/dist/cjs/convert.cjs.map +1 -0
- package/dist/cjs/convert.d.cts +31 -0
- package/dist/cjs/index.cjs +45 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +26 -0
- package/dist/cjs/parser.cjs +195 -0
- package/dist/cjs/parser.cjs.map +1 -0
- package/dist/cjs/parser.d.cts +55 -0
- package/dist/cjs/serialize.cjs +146 -0
- package/dist/cjs/serialize.cjs.map +1 -0
- package/dist/cjs/serialize.d.cts +33 -0
- package/dist/cjs/types.cjs +29 -0
- package/dist/cjs/types.cjs.map +1 -0
- package/dist/cjs/types.d.cts +55 -0
- package/dist/esm/__tests__/conformance.test.d.ts +9 -0
- package/dist/esm/__tests__/conformance.test.d.ts.map +1 -0
- package/dist/esm/__tests__/conformance.test.js +170 -0
- package/dist/esm/__tests__/conformance.test.js.map +1 -0
- package/dist/esm/__tests__/index.test.d.ts +5 -0
- package/dist/esm/__tests__/index.test.d.ts.map +1 -0
- package/dist/esm/__tests__/index.test.js +287 -0
- package/dist/esm/__tests__/index.test.js.map +1 -0
- package/dist/esm/checksum.d.ts +36 -0
- package/dist/esm/checksum.d.ts.map +1 -0
- package/dist/esm/checksum.js +95 -0
- package/dist/esm/checksum.js.map +1 -0
- package/dist/esm/convert.d.ts +32 -0
- package/dist/esm/convert.d.ts.map +1 -0
- package/dist/esm/convert.js +115 -0
- package/dist/esm/convert.js.map +1 -0
- package/dist/esm/index.d.ts +27 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +30 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/parser.d.ts +56 -0
- package/dist/esm/parser.d.ts.map +1 -0
- package/dist/esm/parser.js +189 -0
- package/dist/esm/parser.js.map +1 -0
- package/dist/esm/serialize.d.ts +34 -0
- package/dist/esm/serialize.d.ts.map +1 -0
- package/dist/esm/serialize.js +143 -0
- package/dist/esm/serialize.js.map +1 -0
- package/dist/esm/types.d.ts +56 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +26 -0
- package/dist/esm/types.js.map +1 -0
- 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,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>;
|